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

1786 lines
111 KiB
C#

using System.Data;
using YLErp.BLL;
using YLErp.Commons;
using YLErp.DBModels.Consts;
using YLErp.DBModels.Helpers;
using YLErp.Model;
using YLErp.Modules.DataProviderModule;
using YLErp.Modules.UnderlyingModule;
using YLErp.QdpModule;
namespace YLErp.Modules.TradeModule.DealModule
{
/// <summary>
/// 除权除息操作(请使用正常英文单词)
/// </summary>
public class DividendService : YLBaseService
{
public DividendService(OptUserInfo userInfo) : base(userInfo)
{
}
/// <summary>
/// 场内交易除权除息
/// </summary>
/// <param name="settleDate">除权日</param>
/// <param name="positions">持仓列表</param>
public List<BodTradePosition> Execute(DateTime settleDate, IEnumerable<EodTradePosition> positions)
{
var result = new List<BodTradePosition>();
var dict = DbContext.ex_dividend_info.Where(O => O.ValidStatus && O.ExDividendDate == settleDate).ToDictionary(K => K.UnderlyingId, V => V);
foreach (var item in positions)
{
double cost = item.Cost,
amount = item.Amount;
//averagePrice = item.AveragePrice;
var bodPosition = new BodTradePosition();
if (dict.ContainsKey(item.UnderlyingId))
{
var averagePrice = Math.Abs(item.Cost / item.Amount);
amount = GetPositionAmount(amount, dict[item.UnderlyingId]);
averagePrice = GetPrice(averagePrice, dict[item.UnderlyingId]);
cost = amount * averagePrice;
}
bodPosition.ValueDate = settleDate;
bodPosition.HedgeUniqueCode = item.HedgeUniqueCode;
bodPosition.ExchangeOptionCode = item.ExchangeOptionCode;
bodPosition.TradeType = item.TradeType;
bodPosition.PositionType = item.PositionType;
bodPosition.BookId = item.BookId;
bodPosition.UnderlyingId = item.UnderlyingId;
bodPosition.UnderlyingCode = item.UnderlyingCode;
bodPosition.BuySell = item.BuySell;
bodPosition.Cost = cost;
bodPosition.Amount = amount;
//bodPosition.AveragePrice = averagePrice;
bodPosition.Commission = item.Commission;
bodPosition.OptId = item.OptId;
bodPosition.OptName = item.OptName;
bodPosition.OptDate = DateTime.Now;
result.Add(bodPosition);
}
return result;
}
/// <summary>
/// 场外交易除权除息
/// </summary>
/// <param name="settleDate">除权日</param>
/// <param name="trades">交易列表</param>
/// <param name="saveToDb">是否将结果保存到trade表</param>
/// <returns></returns>
public List<bod_trade> Execute(DateTime settleDate, IEnumerable<trade> trades, out List<trade> useSaveTrades, out List<underlying_manager> useSaveUndedrlyings)
{
useSaveTrades = new List<trade>();
useSaveUndedrlyings = new List<underlying_manager>();
var result = new List<bod_trade>();
var dict = DbContext.ex_dividend_info.Where(O => O.ValidStatus && O.ExDividendDate == settleDate).ToDictionary(K => K.UnderlyingId, V => V);
var tradeIds = trades.Select(O => O.id);
var dividendRatioDict = new DbRecordChangesService<TradeChanges>(this).GetValue(ConsInfoChangeType.UserChange, tradeIds, nameof(trade.DividendRatio), settleDate).ToDictionary(K => K.RecordId, V => { return double.TryParse(V.NewValue, out var temp) ? (double?)temp : null; });
foreach (var t in trades)
{
var bodTrade = new bod_trade();
if (dict.TryGetValue(t.UnderlyingId, out var dividendInfo))
{
var ratio = dividendRatioDict.ContainsKey(t.id) ? dividendRatioDict[t.id] : null;
//避免该字段为NULL时amount和notion不一致
//股票的amount和notion应相等
var unwindNotional = t.OriginalNotional - t.Notional;
t.TradeOriginalAmount = t.TradeOriginalAmount ?? t.OriginalNotional;
t.SpotPrice = GetPrice(t.SpotPrice, dividendInfo, ratio);
var annualizeFactor = t.AnnualizeFactor;
if (t.TradeType == "凤凰期权")
{
annualizeFactor = t.trade_autocall.AnnualizeFactor2;
}
else if (t.TradeType == "雪球期权")
{
annualizeFactor = t.trade_snowball.AnnualizeFactor2;
}
t.StockEqvNotionalReal = t.StockEqvNotionalReal == 0 ? TradeHelper.GetStockEqvNotionalReal(t.OriginalStockEqvNotional, t.ParticipationRate, annualizeFactor) : t.StockEqvNotionalReal;
t.OriginalNotional = t.StockEqvNotionalReal / t.SpotPrice;
t.TradeOriginalAmount = t.OriginalNotional / (t.CountRatio ?? 1);
//不管是不是名义本金方式了结,都应该按照比例了结。--时嬴政
//if (t.IsUsePremiumRate.HasValue && t.IsUsePremiumRate.Value)
//{
var settlePercent = (t.StockEqvNotional / t.OriginalStockEqvNotional) ?? 0;
t.Notional = (t.OriginalNotional * settlePercent) ?? 0;
t.TradeAmount = (t.TradeOriginalAmount * settlePercent) ?? 0;
//}
//else
//{
// t.Notional = (t.OriginalNotional - unwindNotional) ?? 0;
// t.TradeAmount = (t.TradeOriginalAmount - unwindNotional) ?? 0;
//}
t.TradeSinglePrice = TradeHelper.GetTradeSinglePriceByTradePrice(t.TradePrice, t.OriginalNotional, t.PrincipalSum(), t.BuySell, t.TradeType, true);
if (t.TradeType == "彩虹期权")
{
t.trade_rainbow_option.SpotPrice1 = GetPrice(t.trade_rainbow_option.SpotPrice1, dividendInfo, ratio);
t.trade_rainbow_option.SpotPrice2 = GetPrice(t.trade_rainbow_option.SpotPrice2, dividendInfo, ratio);
}
if (!t.IsMoneynessOptionData)
{
t.Strike = GetPrice(t.Strike, dividendInfo, ratio);
switch (t.TradeType)
{
case "亚式期权":
//获取fixings的时候,函数内部处理除权除息逻辑;
break;
case "障碍期权":
t.trade_barrier_option.BarrierPrice = GetPrice(t.trade_barrier_option.BarrierPrice, dividendInfo, ratio);
t.trade_barrier_option.UpperBarrierPrice = GetPrice(t.trade_barrier_option.UpperBarrierPrice, dividendInfo, ratio);
break;
case "彩虹期权":
t.trade_rainbow_option.Strike = GetPrice(t.trade_rainbow_option.Strike, dividendInfo, ratio);
t.trade_rainbow_option.Strike2 = GetPrice(t.trade_rainbow_option.Strike2, dividendInfo, ratio);
break;
case "凤凰期权":
t.trade_autocall.KIBarrier = GetPrice(t.trade_autocall.KIBarrier, dividendInfo, ratio);
t.trade_autocall.KOBarrier = GetPrice(t.trade_autocall.KOBarrier, dividendInfo, ratio);
t.trade_autocall.CouponBarrier = GetPrice(t.trade_autocall.CouponBarrier, dividendInfo, ratio);
t.trade_autocall.SpreadStrike = GetPrice(t.trade_autocall.SpreadStrike, dividendInfo, ratio);
t.trade_autocall.SpreadStrike1 = GetPrice(t.trade_autocall.SpreadStrike1, dividendInfo, ratio);
var obsInfos = QdpHelper.ParseAutocallCustomizedInfo(t.trade_autocall.KOObservationDates);
for (var i = 0; i < obsInfos.Item2.Length; i++)
{
obsInfos.Item2[i] = GetPrice(obsInfos.Item2[i], dividendInfo, ratio);
}
t.trade_autocall.KOObservationDates = $"{string.Join(",", obsInfos.Item1.Select(O => O.ToString()))};{string.Join(",", obsInfos.Item2.Select(O => O.OtcFormatUmPrice()))};{string.Join(",", obsInfos.Item3.Select(O => O.OtcFormatFlex(2)))}";
break;
case "双鲨期权":
t.trade_double_sharkfin_option.BarrierHigh = GetPrice(t.trade_double_sharkfin_option.BarrierHigh, dividendInfo, ratio);
t.trade_double_sharkfin_option.BarrierLow = GetPrice(t.trade_double_sharkfin_option.BarrierLow, dividendInfo, ratio);
t.trade_double_sharkfin_option.StrikeHigh = GetPrice(t.trade_double_sharkfin_option.StrikeHigh, dividendInfo, ratio);
t.trade_double_sharkfin_option.StrikeLow = GetPrice(t.trade_double_sharkfin_option.StrikeLow, dividendInfo, ratio);
break;
case "区间累积期权":
t.trade_rangeaccrual.LowerRange = GetPrice(t.trade_rangeaccrual.LowerRange, dividendInfo, ratio);
t.trade_rangeaccrual.UpperRange = GetPrice(t.trade_rangeaccrual.UpperRange, dividendInfo, ratio);
break;
case "雪球期权":
t.trade_snowball.KIBarrier = GetPrice(t.trade_snowball.KIBarrier, dividendInfo, ratio);
t.trade_snowball.KOBarrier = GetPrice(t.trade_snowball.KOBarrier, dividendInfo, ratio);
t.trade_snowball.SpreadStrikeAtKO = GetPrice(t.trade_snowball.SpreadStrikeAtKO, dividendInfo, ratio);
t.trade_snowball.SpreadStrikeAtKO1 = GetPrice(t.trade_snowball.SpreadStrikeAtKO1, dividendInfo, ratio);
t.trade_snowball.SpreadStrikeAtMaturity = GetPrice(t.trade_snowball.SpreadStrikeAtMaturity, dividendInfo, ratio);
t.trade_snowball.SpreadStrikeAtMaturity1 = GetPrice(t.trade_snowball.SpreadStrikeAtMaturity1, dividendInfo, ratio);
obsInfos = QdpHelper.ParseAutocallCustomizedInfo(t.trade_snowball.KOObservationDates);
for (var i = 0; i < obsInfos.Item2.Length; i++)
{
obsInfos.Item2[i] = GetPrice(obsInfos.Item2[i], dividendInfo, ratio);
}
t.trade_snowball.KOObservationDates = $"{string.Join(",", obsInfos.Item1.Select(O => O.ToString()))};{string.Join(",", obsInfos.Item2.Select(O => O.OtcFormatUmPrice()))};{string.Join(",", obsInfos.Item3.Select(O => O.OtcFormatFlex(2)))}";
break;
case "气囊结构":
t.trade_airbag.Barrier = GetPrice(t.trade_airbag.Barrier, dividendInfo, ratio);
t.trade_airbag.HighStrike = GetPrice(t.trade_airbag.HighStrike, dividendInfo, ratio);
break;
case "累计期权":
t.trade_accumulator_option.KOBarrier = GetPrice(t.trade_accumulator_option.KOBarrier, dividendInfo, ratio);
break;
default:
break;
}
}
if (t.TradeType == "累计期权")
{
var customObservDates = QdpHelper.ParseObservationDate(t.trade_accumulator_option.KOObservationDates);
if (customObservDates == null)
{
customObservDates = Qdp.Pricing.Base.Implementations.CalendarImpl.Get("chn")
.BizDaysBetweenDatesExcluStartDay(t.TradeDate.Value, t.ExerciseDate.Value).ToArray();
}
var unwindPercent = t.trade_accumulator_option.AccumuTradeAmount / t.trade_accumulator_option.OriginalAccumuTradeAmount;
t.trade_accumulator_option.OriginalAccumuTradeAmount = (t.OriginalNotional ?? 0) / (t.CountRatio ?? 1) / customObservDates.Length;
t.trade_accumulator_option.AccumuTradeAmount = t.trade_accumulator_option.OriginalAccumuTradeAmount * unwindPercent;
}
t.DividendDate = settleDate;
useSaveTrades.Add(t);
}
if (t.TradeType == "收益互换")
{
var originalNotional = 0d;
var notional = 0d;
if ((t.trade_swap.PayUnderlyingId ?? 0) > 0)
{
if (t.pay_trade_swap_details?.Count > 0)
{
double? ratio = 0d;
foreach (var item in t.pay_trade_swap_details)
{
var um = DataCacheProvider.GetUnderlyingDataSource().GetData(item.UnderlyingCode);
if (dict.TryGetValue(um.id, out dividendInfo))
{
ratio = dividendRatioDict.ContainsKey(t.id) ? dividendRatioDict[t.id] : null;
var unwindNotional = item.OriginalNotional - item.Notional;
var OriginalStockEqvNotional = item.SpotPrice * item.OriginalNotional;
var StockEqvNotional = item.SpotPrice * item.Notional;
item.SpotPrice = GetPrice(item.SpotPrice, dividendInfo, ratio);
item.OriginalNotional = OriginalStockEqvNotional / item.SpotPrice;
if (t.IsUsePremiumRate.HasValue && t.IsUsePremiumRate.Value)
{
var settlePercent = (StockEqvNotional / OriginalStockEqvNotional) ?? 0;
item.Notional = (item.OriginalNotional * settlePercent) ?? 0;
}
else
{
item.Notional = (item.OriginalNotional - unwindNotional) ?? 0;
}
}
}
originalNotional = t.pay_trade_swap_details.Sum(O => O.OriginalNotional ?? 0);
notional = t.pay_trade_swap_details.Sum(O => O.Notional ?? 0);
if (!useSaveTrades.Contains(t))
{
t.DividendDate = settleDate;
useSaveTrades.Add(t);
}
}
else
{
originalNotional = t.OriginalNotional ?? 1;
notional = t.Notional;
var useDividend = false;
double? ratio = 0d;
var payUm = DataCacheProvider.GetUnderlyingDataSource().GetData(t.trade_swap.PayUnderlyingId.Value);
if (payUm.IsBasket() && payUm.SubData != null)
{
var umchanges = new DbRecordChangesService<UnderlyingChanges>(this).GetValue(ConsInfoChangeType.Dividend, payUm.id, nameof(underlying_manager.SubData), settleDate);
if (umchanges != null)
{
payUm.SubData = umchanges.FieldValue;
}
else
{
useSaveUndedrlyings.Add(payUm);
}
var data = JsonHelper.Deserialize<List<BasketUnderlyingItem>>(payUm.SubData);
var originWeight = data.Sum(O => O.weight);
foreach (var item in data)
{
var tempUm = DataCacheProvider.GetUnderlyingDataSource().GetData(item.code);
if (dict.TryGetValue(tempUm?.id ?? 0, out dividendInfo))
{
ratio = (dividendRatioDict.ContainsKey(t.id) ? dividendRatioDict[t.id] : null) ?? GetRatio(dividendInfo);
item.weight *= ratio.Value;
useDividend = true;
}
}
payUm.SubData = data.ToJson();
useSaveUndedrlyings.Add(payUm);
var newWeight = data.Sum(O => O.weight);
ratio = newWeight / originWeight;
}
else
{
if (dict.TryGetValue(t.trade_swap.PayUnderlyingId ?? 0, out dividendInfo))
{
ratio = dividendRatioDict.ContainsKey(t.id) ? dividendRatioDict[t.id] : null;
useDividend = true;
}
}
if (useDividend)
{
t.trade_swap.PaySpotPrice = GetPrice(t.trade_swap.PaySpotPrice, dividendInfo, ratio);
t.StockEqvNotionalReal = t.StockEqvNotionalReal == 0 ? TradeHelper.GetStockEqvNotionalReal(t.OriginalStockEqvNotional, t.ParticipationRate, t.AnnualizeFactor) : t.StockEqvNotionalReal;
t.trade_swap.PayNotional = t.StockEqvNotionalReal / t.trade_swap.PaySpotPrice;
//设置交易数量
var underlyingManager = DataCacheProvider.GetUnderlyingDataSource().GetData(t.trade_swap.PayUnderlyingCode);
var CountRatio = underlyingManager?.CountRatio ?? 1;
t.trade_swap.PayTradeAmount = t.trade_swap.PayNotional / CountRatio;
t.DividendDate = settleDate;
originalNotional = NumberHelper.Normalize(t.OriginalStockEqvNotional / t.trade_swap.PaySpotPrice);
notional = NumberHelper.Normalize(t.StockEqvNotional / t.trade_swap.PaySpotPrice);
if (!useSaveTrades.Contains(t))
{
t.DividendDate = settleDate;
useSaveTrades.Add(t);
}
}
}
}
if ((t.trade_swap.GetUnderlyingId ?? 0) > 0)
{
if (t.get_trade_swap_details?.Count > 0)
{
double? ratio = 0d;
foreach (var item in t.get_trade_swap_details)
{
var um = DataCacheProvider.GetUnderlyingDataSource().GetData(item.UnderlyingCode);
if (dict.TryGetValue(um.id, out dividendInfo))
{
ratio = dividendRatioDict.ContainsKey(t.id) ? dividendRatioDict[t.id] : null;
var unwindNotional = item.OriginalNotional - item.Notional;
var OriginalStockEqvNotional = item.SpotPrice * item.OriginalNotional;
var StockEqvNotional = item.SpotPrice * item.Notional;
item.SpotPrice = GetPrice(item.SpotPrice, dividendInfo, ratio);
item.OriginalNotional = OriginalStockEqvNotional / item.SpotPrice;
if (t.IsUsePremiumRate.HasValue && t.IsUsePremiumRate.Value)
{
var settlePercent = (StockEqvNotional / OriginalStockEqvNotional) ?? 0;
item.Notional = (item.OriginalNotional * settlePercent) ?? 0;
}
else
{
item.Notional = (item.OriginalNotional - unwindNotional) ?? 0;
}
}
}
originalNotional = t.get_trade_swap_details.Sum(O => O.OriginalNotional ?? 0);
notional = t.get_trade_swap_details.Sum(O => O.Notional ?? 0);
if (!useSaveTrades.Contains(t))
{
t.DividendDate = settleDate;
useSaveTrades.Add(t);
}
}
else
{
originalNotional = t.OriginalNotional ?? 1;
notional = t.Notional;
double? ratio = 0d;
var useDividend = false;
var getUm = DataCacheProvider.GetUnderlyingDataSource().GetData(t.trade_swap.GetUnderlyingId.Value);
if (getUm.IsBasket() && getUm.SubData != null)
{
var umchanges = new DbRecordChangesService<UnderlyingChanges>(this).GetValue(ConsInfoChangeType.Dividend, getUm.id, nameof(underlying_manager.SubData), settleDate);
if (umchanges != null)
{
getUm.SubData = umchanges.FieldValue;
}
else
{
useSaveUndedrlyings.Add(getUm);
}
var data = JsonHelper.Deserialize<List<BasketUnderlyingItem>>(getUm.SubData);
var originWeight = data.Sum(O => O.weight);
foreach (var item in data)
{
var tempUm = DataCacheProvider.GetUnderlyingDataSource().GetData(item.code);
if (dict.TryGetValue(tempUm?.id ?? 0, out dividendInfo))
{
ratio = (dividendRatioDict.ContainsKey(t.id) ? dividendRatioDict[t.id] : null) ?? GetRatio(dividendInfo);
item.weight *= ratio.Value;
useDividend = true;
}
}
getUm.SubData = data.ToJson();
var newWeight = data.Sum(O => O.weight);
ratio = newWeight / originWeight;
}
else
{
if (dict.TryGetValue(t.trade_swap.GetUnderlyingId ?? 0, out dividendInfo))
{
ratio = dividendRatioDict.ContainsKey(t.id) ? dividendRatioDict[t.id] : null;
useDividend = true;
}
}
if (useDividend)
{
t.trade_swap.GetSpotPrice = GetPrice(t.trade_swap.GetSpotPrice, dividendInfo, ratio);
t.StockEqvNotionalReal = t.StockEqvNotionalReal == 0 ? TradeHelper.GetStockEqvNotionalReal(t.OriginalStockEqvNotional, t.ParticipationRate, t.AnnualizeFactor) : t.StockEqvNotionalReal;
t.trade_swap.GetNotional = t.StockEqvNotionalReal / t.trade_swap.GetSpotPrice;
//设置交易数量
var underlyingManager = DataCacheProvider.GetUnderlyingDataSource().GetData(t.trade_swap.GetUnderlyingCode);
var CountRatio = underlyingManager?.CountRatio ?? 1;
t.trade_swap.GetTradeAmount = t.trade_swap.GetNotional / CountRatio;
t.DividendDate = settleDate;
originalNotional = NumberHelper.Normalize(t.OriginalStockEqvNotional / t.trade_swap.GetSpotPrice);
notional = NumberHelper.Normalize(t.StockEqvNotional / t.trade_swap.GetSpotPrice);
if (!useSaveTrades.Contains(t))
{
t.DividendDate = settleDate;
useSaveTrades.Add(t);
}
}
}
}
t.OriginalNotional = originalNotional;
t.TradeOriginalAmount = originalNotional;
t.Notional = notional;
}
bodTrade.AssetId = t.AssetId;
bodTrade.ClientId = t.ClientId;
bodTrade.OptDate = DateTime.Now;
bodTrade.ParentTradeId = t.ParentTradeId;
bodTrade.TradeId = t.id;
bodTrade.TradeNumber = t.TradeNumber;
bodTrade.TradeStatus = t.TradeStatus;
bodTrade.TradeType = t.TradeType;
bodTrade.UnderlyingCode = t.UnderlyingCode;
bodTrade.UnderlyingId = t.UnderlyingId;
bodTrade.ValueDate = settleDate;
bodTrade.TradeJson = TradeHelper2.Serialize(t, settleDate);
result.Add(bodTrade);
}
return result;
}
/// <summary>
///
/// </summary>
public static List<TradeChanges> GetDividendChanges(trade trade, trade NewValue)
{
TradeChanges compar(double? originValue, double? newValue, params string[] fieldName)
{
TradeChanges tc = null;
if ((originValue == null && newValue != null) || (originValue != null && newValue == null) || originValue != newValue || (ConsGlobal.IgnoreTSV((originValue ?? 0) - (newValue ?? 0)) != 0))
{
tc = new TradeChanges
{
ChangeType = ConsInfoChangeType.Dividend,
RecordId = trade.id,
FieldName = string.Join(".", fieldName),
FieldValue = originValue?.ToString() ?? "",
NewValue = newValue?.ToString() ?? "",
OptDate = NewValue.DividendDate,
Update = DateTime.Now
};
}
return tc;
}
TradeChanges comparStr(string originValue, string newValue, params string[] fieldName)
{
TradeChanges tc = null;
if ((originValue == null && newValue != null) || (originValue != null && newValue == null) || originValue != newValue)
{
tc = new TradeChanges
{
ChangeType = ConsInfoChangeType.Dividend,
RecordId = trade.id,
FieldName = string.Join(".", fieldName),
FieldValue = originValue ?? "",
NewValue = newValue ?? "",
OptDate = NewValue.DividendDate,
Update = DateTime.Now
};
}
return tc;
}
var list = new List<TradeChanges>
{
compar(trade.SpotPrice, NewValue.SpotPrice, nameof(trade.SpotPrice)),
compar(trade.OriginalNotional, NewValue.OriginalNotional, nameof(trade.OriginalNotional)),
compar(trade.TradeOriginalAmount ?? trade.OriginalNotional, NewValue.TradeOriginalAmount, nameof(trade.TradeOriginalAmount)),
compar(trade.Notional, NewValue.Notional, nameof(trade.Notional)),
compar(trade.TradeAmount, NewValue.TradeAmount, nameof(trade.TradeAmount)),
compar(trade.TradeSinglePrice, NewValue.TradeSinglePrice, nameof(trade.TradeSinglePrice)),
compar(trade.Strike, NewValue.Strike, nameof(trade.Strike))
};
switch (trade.TradeType)
{
case "障碍期权":
list.Add(compar(trade.trade_barrier_option.BarrierPrice, NewValue.trade_barrier_option.BarrierPrice, nameof(trade.trade_barrier_option), nameof(trade.trade_barrier_option.BarrierPrice)));
list.Add(compar(trade.trade_barrier_option.UpperBarrierPrice, NewValue.trade_barrier_option.UpperBarrierPrice, nameof(trade.trade_barrier_option), nameof(trade.trade_barrier_option.UpperBarrierPrice)));
break;
case "彩虹期权":
list.Add(compar(trade.trade_rainbow_option.SpotPrice1, NewValue.trade_rainbow_option.SpotPrice1, nameof(trade.trade_rainbow_option), nameof(trade.trade_rainbow_option.SpotPrice1)));
list.Add(compar(trade.trade_rainbow_option.SpotPrice2, NewValue.trade_rainbow_option.SpotPrice2, nameof(trade.trade_rainbow_option), nameof(trade.trade_rainbow_option.SpotPrice2)));
list.Add(compar(trade.trade_rainbow_option.Strike, NewValue.trade_rainbow_option.Strike, nameof(trade.trade_rainbow_option), nameof(trade.trade_rainbow_option.Strike)));
list.Add(compar(trade.trade_rainbow_option.Strike2, NewValue.trade_rainbow_option.Strike2, nameof(trade.trade_rainbow_option), nameof(trade.trade_rainbow_option.Strike2)));
break;
case "凤凰期权":
list.Add(compar(trade.trade_autocall.KIBarrier, NewValue.trade_autocall.KIBarrier, nameof(trade.trade_autocall), nameof(trade.trade_autocall.KIBarrier)));
list.Add(compar(trade.trade_autocall.KOBarrier, NewValue.trade_autocall.KOBarrier, nameof(trade.trade_autocall), nameof(trade.trade_autocall.KOBarrier)));
list.Add(compar(trade.trade_autocall.CouponBarrier, NewValue.trade_autocall.CouponBarrier, nameof(trade.trade_autocall), nameof(trade.trade_autocall.CouponBarrier)));
list.Add(compar(trade.trade_autocall.SpreadStrike, NewValue.trade_autocall.SpreadStrike, nameof(trade.trade_autocall), nameof(trade.trade_autocall.SpreadStrike)));
list.Add(compar(trade.trade_autocall.SpreadStrike1, NewValue.trade_autocall.SpreadStrike, nameof(trade.trade_autocall), nameof(trade.trade_autocall.SpreadStrike1)));
list.Add(comparStr(trade.trade_autocall.KOObservationDates, NewValue.trade_autocall.KOObservationDates, nameof(trade.trade_autocall), nameof(trade.trade_autocall.KOObservationDates)));
break;
case "双鲨期权":
list.Add(compar(trade.trade_double_sharkfin_option.BarrierHigh, NewValue.trade_double_sharkfin_option.BarrierHigh, nameof(trade.trade_double_sharkfin_option), nameof(trade.trade_double_sharkfin_option.BarrierHigh)));
list.Add(compar(trade.trade_double_sharkfin_option.BarrierLow, NewValue.trade_double_sharkfin_option.BarrierLow, nameof(trade.trade_double_sharkfin_option), nameof(trade.trade_double_sharkfin_option.BarrierLow)));
list.Add(compar(trade.trade_double_sharkfin_option.StrikeHigh, NewValue.trade_double_sharkfin_option.StrikeHigh, nameof(trade.trade_double_sharkfin_option), nameof(trade.trade_double_sharkfin_option.StrikeHigh)));
list.Add(compar(trade.trade_double_sharkfin_option.StrikeLow, NewValue.trade_double_sharkfin_option.StrikeLow, nameof(trade.trade_double_sharkfin_option), nameof(trade.trade_double_sharkfin_option.StrikeLow)));
break;
case "区间累积期权":
list.Add(compar(trade.trade_rangeaccrual.LowerRange, NewValue.trade_rangeaccrual.LowerRange, nameof(trade.trade_rangeaccrual), nameof(trade.trade_rangeaccrual.LowerRange)));
list.Add(compar(trade.trade_rangeaccrual.UpperRange, NewValue.trade_rangeaccrual.UpperRange, nameof(trade.trade_rangeaccrual), nameof(trade.trade_rangeaccrual.UpperRange)));
break;
case "雪球期权":
list.Add(compar(trade.trade_snowball.KIBarrier, NewValue.trade_snowball.KIBarrier, nameof(trade.trade_snowball), nameof(trade.trade_snowball.KIBarrier)));
list.Add(compar(trade.trade_snowball.KOBarrier, NewValue.trade_snowball.KOBarrier, nameof(trade.trade_snowball), nameof(trade.trade_snowball.KOBarrier)));
list.Add(compar(trade.trade_snowball.SpreadStrikeAtKO, NewValue.trade_snowball.SpreadStrikeAtKO, nameof(trade.trade_snowball), nameof(trade.trade_snowball.SpreadStrikeAtKO)));
list.Add(compar(trade.trade_snowball.SpreadStrikeAtKO1, NewValue.trade_snowball.SpreadStrikeAtKO1, nameof(trade.trade_snowball), nameof(trade.trade_snowball.SpreadStrikeAtKO1)));
list.Add(compar(trade.trade_snowball.SpreadStrikeAtMaturity, NewValue.trade_snowball.SpreadStrikeAtMaturity, nameof(trade.trade_snowball), nameof(trade.trade_snowball.SpreadStrikeAtMaturity)));
list.Add(compar(trade.trade_snowball.SpreadStrikeAtMaturity1, NewValue.trade_snowball.SpreadStrikeAtMaturity1, nameof(trade.trade_snowball), nameof(trade.trade_snowball.SpreadStrikeAtMaturity1)));
list.Add(comparStr(trade.trade_snowball.KOObservationDates, NewValue.trade_snowball.KOObservationDates, nameof(trade.trade_snowball), nameof(trade.trade_snowball.KOObservationDates)));
break;
case "气囊结构":
list.Add(compar(trade.trade_airbag.Barrier, NewValue.trade_airbag.Barrier, nameof(trade.trade_airbag), nameof(trade.trade_airbag.Barrier)));
list.Add(compar(trade.trade_airbag.HighStrike, NewValue.trade_airbag.HighStrike, nameof(trade.trade_airbag), nameof(trade.trade_airbag.HighStrike)));
break;
case "收益互换":
if (trade.trade_swap != null)
{
list.Add(compar(trade.trade_swap.PaySpotPrice, NewValue.trade_swap.PaySpotPrice, nameof(trade.trade_swap), nameof(trade.trade_swap.PaySpotPrice)));
list.Add(compar(trade.trade_swap.PayNotional, NewValue.trade_swap.PayNotional, nameof(trade.trade_swap), nameof(trade.trade_swap.PayNotional)));
list.Add(compar(trade.trade_swap.PayTradeAmount, NewValue.trade_swap.PayTradeAmount, nameof(trade.trade_swap), nameof(trade.trade_swap.PayTradeAmount)));
if (trade.pay_trade_swap_details != null)
{
foreach (var item in trade.pay_trade_swap_details)
{
if (!ConsGlobal.IsValid(item.ValidState))
{
continue;
}
var newDetail = NewValue.pay_trade_swap_details.Find(O => O.id == item.id);
list.Add(compar(item.SpotPrice, newDetail.SpotPrice, $"{nameof(trade.pay_trade_swap_details)}[{item.id}]", nameof(trade_swap_detail.SpotPrice)));
list.Add(compar(item.OriginalNotional, newDetail.OriginalNotional, $"{nameof(trade.pay_trade_swap_details)}[{item.id}]", nameof(trade_swap_detail.OriginalNotional)));
list.Add(compar(item.Notional, newDetail.Notional, $"{nameof(trade.pay_trade_swap_details)}[{item.id}]", nameof(trade_swap_detail.Notional)));
}
}
list.Add(compar(trade.trade_swap.GetSpotPrice, NewValue.trade_swap.GetSpotPrice, nameof(trade.trade_swap), nameof(trade.trade_swap.GetSpotPrice)));
list.Add(compar(trade.trade_swap.GetNotional, NewValue.trade_swap.GetNotional, nameof(trade.trade_swap), nameof(trade.trade_swap.GetNotional)));
list.Add(compar(trade.trade_swap.GetTradeAmount, NewValue.trade_swap.GetTradeAmount, nameof(trade.trade_swap), nameof(trade.trade_swap.GetTradeAmount)));
if (trade.get_trade_swap_details != null)
{
foreach (var item in trade.get_trade_swap_details)
{
if (!ConsGlobal.IsValid(item.ValidState))
{
continue;
}
var newDetail = NewValue.get_trade_swap_details.Find(O => O.id == item.id);
list.Add(compar(item.SpotPrice, newDetail.SpotPrice, $"{nameof(trade.get_trade_swap_details)}[{item.id}]", nameof(trade_swap_detail.SpotPrice)));
list.Add(compar(item.OriginalNotional, newDetail.OriginalNotional, $"{nameof(trade.get_trade_swap_details)}[{item.id}]", nameof(trade_swap_detail.OriginalNotional)));
list.Add(compar(item.Notional, newDetail.Notional, $"{nameof(trade.get_trade_swap_details)}[{item.id}]", nameof(trade_swap_detail.Notional)));
}
}
}
break;
case "累计期权":
list.Add(compar(trade.trade_accumulator_option.KOBarrier, NewValue.trade_accumulator_option.AccumuTradeAmount, nameof(trade.trade_accumulator_option), nameof(trade.trade_accumulator_option.KOBarrier)));
list.Add(compar(trade.trade_accumulator_option.AccumuTradeAmount, NewValue.trade_accumulator_option.AccumuTradeAmount, nameof(trade.trade_accumulator_option), nameof(trade.trade_accumulator_option.AccumuTradeAmount)));
list.Add(compar(trade.trade_accumulator_option.OriginalAccumuTradeAmount, NewValue.trade_accumulator_option.OriginalAccumuTradeAmount, nameof(trade.trade_accumulator_option), nameof(trade.trade_accumulator_option.OriginalAccumuTradeAmount)));
break;
default:
break;
}
list.RemoveAll(O => O == null);
return list;
}
/// <summary>
///
/// </summary>
public static void SetDividendTradeData(trade trade, trade NewValue)
{
trade.SpotPrice = NewValue.SpotPrice;
trade.OriginalNotional = NewValue.OriginalNotional;
trade.OriginalStockEqvNotional = NewValue.OriginalStockEqvNotional;
trade.StockEqvNotionalReal = NewValue.StockEqvNotionalReal;
trade.StockEqvNotional = NewValue.StockEqvNotional;
trade.Notional = NewValue.Notional;
trade.TradeSinglePrice = TradeHelper.GetTradeSinglePriceByTradePrice(NewValue.TradePrice, NewValue.OriginalNotional, NewValue.PrincipalSum(), NewValue.BuySell, NewValue.TradeType, true);
trade.TradeOriginalAmount = NewValue.TradeOriginalAmount;
trade.TradeAmount = NewValue.TradeAmount;
trade.Strike = NewValue.Strike;
trade.DividendDate = NewValue.DividendDate;
switch (trade.TradeType)
{
case "亚式期权":
//获取fixings的时候,函数内部处理除权除息逻辑;
break;
case "障碍期权":
trade.trade_barrier_option.BarrierPrice = NewValue.trade_barrier_option.BarrierPrice;
trade.trade_barrier_option.UpperBarrierPrice = NewValue.trade_barrier_option.UpperBarrierPrice;
break;
case "彩虹期权":
trade.trade_rainbow_option.SpotPrice1 = NewValue.trade_rainbow_option.SpotPrice1;
trade.trade_rainbow_option.SpotPrice2 = NewValue.trade_rainbow_option.SpotPrice2;
trade.trade_rainbow_option.Strike = NewValue.trade_rainbow_option.Strike;
trade.trade_rainbow_option.Strike2 = NewValue.trade_rainbow_option.Strike2;
break;
case "凤凰期权":
trade.trade_autocall.KIBarrier = NewValue.trade_autocall.KIBarrier;
trade.trade_autocall.KOBarrier = NewValue.trade_autocall.KOBarrier;
trade.trade_autocall.CouponBarrier = NewValue.trade_autocall.CouponBarrier;
trade.trade_autocall.SpreadStrike = NewValue.trade_autocall.SpreadStrike;
trade.trade_autocall.SpreadStrike1 = NewValue.trade_autocall.SpreadStrike1;
trade.trade_autocall.KOObservationDates = NewValue.trade_autocall.KOObservationDates;
break;
case "双鲨期权":
trade.trade_double_sharkfin_option.BarrierHigh = NewValue.trade_double_sharkfin_option.BarrierHigh;
trade.trade_double_sharkfin_option.BarrierLow = NewValue.trade_double_sharkfin_option.BarrierLow;
trade.trade_double_sharkfin_option.StrikeHigh = NewValue.trade_double_sharkfin_option.StrikeHigh;
trade.trade_double_sharkfin_option.StrikeLow = NewValue.trade_double_sharkfin_option.StrikeLow;
break;
case "区间累积期权":
trade.trade_rangeaccrual.LowerRange = NewValue.trade_rangeaccrual.LowerRange;
trade.trade_rangeaccrual.UpperRange = NewValue.trade_rangeaccrual.UpperRange;
break;
case "雪球期权":
trade.trade_snowball.KIBarrier = NewValue.trade_snowball.KIBarrier;
trade.trade_snowball.KOBarrier = NewValue.trade_snowball.KOBarrier;
trade.trade_snowball.SpreadStrikeAtKO = NewValue.trade_snowball.SpreadStrikeAtKO;
trade.trade_snowball.SpreadStrikeAtKO1 = NewValue.trade_snowball.SpreadStrikeAtKO1;
trade.trade_snowball.SpreadStrikeAtMaturity = NewValue.trade_snowball.SpreadStrikeAtMaturity;
trade.trade_snowball.SpreadStrikeAtMaturity1 = NewValue.trade_snowball.SpreadStrikeAtMaturity1;
trade.trade_snowball.KOObservationDates = NewValue.trade_snowball.KOObservationDates;
break;
case "气囊结构":
trade.trade_airbag.Barrier = NewValue.trade_airbag.Barrier;
trade.trade_airbag.HighStrike = NewValue.trade_airbag.HighStrike;
break;
case "收益互换":
if (trade.trade_swap != null)
{
trade.trade_swap.PaySpotPrice = NewValue.trade_swap.PaySpotPrice;
trade.trade_swap.PayNotional = NewValue.trade_swap.PayNotional;
trade.trade_swap.PayTradeAmount = NewValue.trade_swap.PayTradeAmount;
if (trade.pay_trade_swap_details != null)
{
foreach (var item in trade.pay_trade_swap_details)
{
if (!ConsGlobal.IsValid(item.ValidState))
{
continue;
}
var newDetail = NewValue.pay_trade_swap_details.Find(O => O.id == item.id);
item.SpotPrice = newDetail.SpotPrice;
item.OriginalNotional = newDetail.OriginalNotional;
item.Notional = newDetail.Notional;
}
}
trade.trade_swap.GetSpotPrice = NewValue.trade_swap.GetSpotPrice;
trade.trade_swap.GetNotional = NewValue.trade_swap.GetNotional;
trade.trade_swap.GetTradeAmount = NewValue.trade_swap.GetTradeAmount;
if (trade.get_trade_swap_details != null)
{
foreach (var item in trade.get_trade_swap_details)
{
if (!ConsGlobal.IsValid(item.ValidState))
{
continue;
}
var newDetail = NewValue.get_trade_swap_details.Find(O => O.id == item.id);
item.SpotPrice = newDetail.SpotPrice;
item.OriginalNotional = newDetail.OriginalNotional;
item.Notional = newDetail.Notional;
}
}
}
break;
case "累计期权":
trade.trade_accumulator_option.KOBarrier = NewValue.trade_accumulator_option.KOBarrier;
trade.trade_accumulator_option.AccumuTradeAmount = NewValue.trade_accumulator_option.AccumuTradeAmount;
trade.trade_accumulator_option.OriginalAccumuTradeAmount = NewValue.trade_accumulator_option.OriginalAccumuTradeAmount;
break;
default:
break;
}
}
/// <summary>
/// 获取除权价
/// </summary>
/// <param name="closePrice">除权日收盘价</param>
/// <param name="info">除权除息配置</param>
/// <param name="overrideDividendRatio">除权除息系数</param>
/// <returns></returns>
public double? GetPrice(double? closePrice, ex_dividend_info info, double? overrideDividendRatio = null)
{
if (!closePrice.HasValue) { return closePrice; }
return GetPrice(closePrice.Value, info, overrideDividendRatio);
}
/// <summary>
/// 获取除权价
/// </summary>
/// <param name="pirce">除权日收盘价</param>
/// <param name="info">除权除息配置</param>
/// <returns></returns>
public double GetPrice(double price, ex_dividend_info info, double? overrideDividendRatio = null)
{
if (price == 0)
{
return 0;
}
var ratio = overrideDividendRatio != null ? overrideDividendRatio.Value : GetRatio(info);
double? result = price / ratio;
return Math.Round(result ?? 0, 4, MidpointRounding.AwayFromZero);
}
/// <summary>
/// 获取除权除息系数
/// </summary>
/// <param name="info"></param>
/// <returns></returns>
public double GetRatio(ex_dividend_info info)
{
var dividendRate = valuedateBLL.SystemDate.DividendRate / 100;
var closePrice = new EodPriceProvider(info.ExDividendDate.Value).GetPrice(info.UnderlyingCode, SettlementTypeEnum.ClosePrice);
var cDivdPrice = (closePrice * 10.0 - (info.GiveCashAmount * (1 - dividendRate)) + info.RationedSharesAmount * info.RationedSharesPrice) / (10 + info.GiveShareAmount + info.RationedSharesAmount);
return closePrice / cDivdPrice;
}
/// <summary>
/// 获取除权后持仓量
/// </summary>
/// <param name="amount">持仓数量</param>
/// <param name="info">除权除息配置</param>
/// <returns></returns>
public double? GetPositionAmount(double? amount, ex_dividend_info info)
{
if (!amount.HasValue) { return amount; }
return GetPositionAmount(amount.Value, info);
}
/// <summary>
/// 获取除权后持仓量
/// </summary>
/// <param name="amount">持仓数量</param>
/// <param name="info">除权除息配置</param>
/// <returns></returns>
public double GetPositionAmount(double amount, ex_dividend_info info)
{
double? result = amount * (1 + info.GiveShareAmount / 10.0);
return Math.Round(result ?? 0, 12);
}
public IQueryable<ex_dividend_info> GetExDividendQuery(DateTime valueDate)
{
return DbContext.ex_dividend_info.Where(O => O.ValidStatus && O.ExDividendDate == valueDate);
}
public IQueryable<ex_dividend_info> GetExDividendQuery(DateTime dateStart, DateTime dateEnd)
{
return DbContext.ex_dividend_info.Where(O => O.ValidStatus && O.ExDividendDate >= dateStart && O.ExDividendDate <= dateEnd);
}
public IEnumerable<ex_dividend_info> GetExDividends(DateTime valueDate, params int[] underlyingIds)
{
var query = DbContext.ex_dividend_info.Where(O => O.ValidStatus && O.ExDividendDate == valueDate);
if (underlyingIds != null && underlyingIds.Any())
{
query = query.Where(n => underlyingIds.Contains(n.UnderlyingId));
}
return query.ToArray();
}
public IQueryable<ex_dividend_info> GetExDividendInfos(string underlyingCode)
{
if (string.IsNullOrWhiteSpace(underlyingCode))
{
throw new ArgumentNullException(nameof(underlyingCode));
}
return
from di in DbContext.ex_dividend_info
join un in DbContext.underlying_manager
on di.UnderlyingId equals un.id
where di.ValidStatus && un.UnderlyingCode == underlyingCode
select di;
}
public void ImportDividendInfos(Stream stream)
{
var dt = new ExcelHelper().ExcelToDataTable(stream, null, true);
if (!dt.Columns.Contains("股票代码") || !dt.Columns.Contains("股权登记日"))
{
throw new ServiceException("请使用正确的模板上传");
}
var dict = new Dictionary<string, ex_dividend_info>();
for (var i = 0; i < dt.Rows.Count; i++)
{
var info = new ex_dividend_info
{
UnderlyingCode = dt.Rows[i]["股票代码"]?.ToString(),
ExDividendDate = DateTime.TryParse(getColValueFromTable(dt.Rows[i], "股权登记日"), out var date) ? date : DateTime.MinValue,
GiveCashAmount = double.TryParse(getColValueFromTable(dt.Rows[i], "派息金额"), out var value) ? value : 0,
GiveShareAmount = double.TryParse(getColValueFromTable(dt.Rows[i], "送股股数"), out value) ? value : 0,
RationedSharesAmount = double.TryParse(getColValueFromTable(dt.Rows[i], "配股股数"), out value) ? value : 0,
RationedSharesPrice = double.TryParse(getColValueFromTable(dt.Rows[i], "配股股价"), out value) ? value : 0,
OptId = OptUser.UserId,
OptName = OptUser.UserName,
OptDate = DateTime.Now
};
if (string.IsNullOrWhiteSpace(info.UnderlyingCode) && info.ExDividendDate < DateTime.Now.AddYears(10))
{
continue;
}
if (string.IsNullOrWhiteSpace(info.UnderlyingCode))
{
throw new ServiceException($"第{i + 1}行股票代码不应为空");
}
if (info.ExDividendDate < DateTime.Now.AddYears(-10))
{
throw new ServiceException($"第{i + 1}行股权登记日不正确");
}
dict[$"{info.ExDividendDate}{info.UnderlyingCode}"] = info;
}
if (!AddDividendInfos(dict.Values, out var errMsg))
{
throw new ServiceException(errMsg);
}
}
private string getColValueFromTable(DataRow dr, string colName)
{
if (dr.Table.Columns.Contains(colName))
{
return dr[colName].ToString();
}
return "";
}
public bool AddDividendInfos(IEnumerable<ex_dividend_info> infos, out string errMsg)
{
try
{
var keys = infos.Select(O => $"{O.ExDividendDate?.ToString("yyyy-MM-dd")}{O.UnderlyingCode}");
var ids = infos.Select(O => O.id).ToHashSet();
var data = from dividendDb in DbContext.ex_dividend_info.Where(O => keys.Contains(O.ExDividendDate + O.UnderlyingCode) && O.ValidStatus)
where !ids.Contains(dividendDb.id)
select dividendDb;
if (data.Any())
{
var dd = data.Select(O => O.UnderlyingCode + "_" + O.ExDividendDate).ToArray();
errMsg = string.Join(",", dd) + "已存在除息信息,请修改原数据";
return false;
}
var basketList =
DataCacheProvider.GetUnderlyingDataSource()
.AsQueryable().Where(O => O.IsBasket() && O.SubData != null)
.Select(O => new { O.UnderlyingCode, O.SubData });
IEnumerable<eod_stock_price> priceList = null;
foreach (var item in infos)
{
var underlying = underlying_managerBLL.GetByCode(item.UnderlyingCode);
if (underlying == null)
{
errMsg = $"{item.UnderlyingCode} 标的信息不存在";
return false;
}
item.UnderlyingId = underlying.id;
item.GiveCashAmount = item.GiveCashAmount.FormatValue(6);
item.RationedSharesAmount = item.RationedSharesAmount.FormatValue(6);
item.RationedSharesPrice = item.RationedSharesPrice.FormatValue(6);
item.GiveShareAmount = item.GiveShareAmount.FormatValue(6);
item.ValidStatus = true;
item.OptId = OptUser.UserId;
item.OptName = OptUser.UserName;
item.OptDate = DateTime.Now;
var dividend = item.id > 0 ? DbContext.ex_dividend_info.Where(O => O.id == item.id).FirstOrDefault() : null;
if (dividend == null)
{ DbContext.ex_dividend_info.Add(item); }
else
{
if (checkDividendInfoExecuteStatus(dividend))
{
errMsg = $"{dividend.UnderlyingCode} {dividend.ExDividendDate?.ToString("yyyy-MM-dd")}除权信息保存失败,该信息已被执行,不允许修改!";
return false;
}
dividend.UnderlyingCode = item.UnderlyingCode;
dividend.UnderlyingId = item.UnderlyingId;
dividend.ExDividendDate = item.ExDividendDate;
dividend.GiveCashAmount = item.GiveCashAmount;
dividend.RationedSharesAmount = item.RationedSharesAmount;
dividend.RationedSharesPrice = item.RationedSharesPrice;
dividend.GiveShareAmount = item.GiveShareAmount;
dividend.ValidStatus = item.ValidStatus;
dividend.OptId = item.OptId;
dividend.OptName = item.OptName;
dividend.OptDate = item.OptDate;
}
if (!basketList.Any())
{
continue;
}
var codes = basketList.Where(O => O.SubData.Contains(item.UnderlyingCode)).Select(O => O.UnderlyingCode);
if (!codes.Any())
{
continue;
}
var removePriceList = DbContext.eod_stock_price.Where(O => codes.Contains(O.UnderlyingCode) && O.ValueDate > item.ExDividendDate);
if (!removePriceList.Any())
{
continue;
}
DbContext.eod_stock_price.RemoveRange(removePriceList);
}
if (priceList != null)
{
DbContext.eod_stock_price.RemoveRange(priceList);
}
DbContext.SaveChanges();
errMsg = "";
return true;
}
catch (Exception ex)
{
errMsg = "保存失败";
LogFactory.GetLogger<DividendService>().Error(ex);
}
return false;
}
/// <summary>
/// 检查除权除息记录有没有被执行
/// </summary>
/// <param name="info"></param>
/// <returns></returns>
public bool checkDividendInfoExecuteStatus(ex_dividend_info info)
{
var eodStatus = DbContext.eodStatus.Where(O => O.ValueDate == info.ExDividendDate && O.OptDate > info.OptDate).Any();
if (eodStatus)
{
//查询标的对应交易是否执行过收盘操作;
var tradeQuery = from t in DbContext.trade.Where(O => O.UnderlyingCode == info.UnderlyingCode && O.TradeDate <= info.ExDividendDate && O.ExerciseDate >= info.ExDividendDate && O.DividendDate >= O.TradeDate)
join et in DbContext.eod_trade.Where(O => ConsTrade.LiveTradeStatusList.Contains(O.TradeStatus))
on new { t.id, ValueDate = t.TradeDate.Value } equals new { id = et.TradeId, et.ValueDate }
select et.id;
if (tradeQuery.Any())
{
return true;
}
//查询篮子标的对应交易是否执行过收盘操作;
var umList = DataCacheProvider.GetUnderlyingDataSource().AsQueryable(O => O.IsBasket() && O.SubData != null && O.SubData.Contains(info.UnderlyingCode)).Select(O => O.UnderlyingCode).ToArray();
tradeQuery = from t in DbContext.trade.Where(O => umList.Contains(O.UnderlyingCode) && O.TradeDate <= info.ExDividendDate && O.ExerciseDate >= info.ExDividendDate && O.DividendDate >= O.TradeDate)
join et in DbContext.eod_trade.Where(O => ConsTrade.LiveTradeStatusList.Contains(O.TradeStatus))
on new { t.id, ValueDate = t.TradeDate.Value } equals new { id = et.TradeId, et.ValueDate }
select et.id;
if (tradeQuery.Any())
{
return true;
}
//查询多标的对应交易是否执行过收盘操作;
tradeQuery = from ts in DbContext.trade_swap_detail.Where(O => O.UnderlyingCode == info.UnderlyingCode)
join t in DbContext.trade.Where(O => O.TradeDate <= info.ExDividendDate && O.ExerciseDate >= info.ExDividendDate && O.DividendDate >= O.TradeDate)
on ts.TradeId equals t.id
join et in DbContext.eod_trade.Where(O => ConsTrade.LiveTradeStatusList.Contains(O.TradeStatus))
on new { t.id, ValueDate = t.TradeDate.Value } equals new { id = et.TradeId, et.ValueDate }
select et.id;
if (tradeQuery.Any())
{
return true;
}
}
return false;
}
public List<DividendTrade> QueryDividendTrade(DividendTradeReq req)
{
TradeChanges changes = null;
var result = new List<DividendTrade>();
var predicate = PredicateBuilder.Create<trade>(O => O.ValidState != "InValid"
&& ConsTrade.TradeStatusAfterConfirmed.Contains(O.TradeStatus)
&& O.TradeType != "结构化交易");
if (!string.IsNullOrWhiteSpace(req.TradeNumber))
{
predicate = predicate.And(O => O.TradeNumber == req.TradeNumber);
}
if (req.ClientIds != null && req.ClientIds.Any(O => O > 0))
{
predicate = predicate.And(O => req.ClientIds.Contains(O.ClientId));
}
if (req.AssetUnitIds != null && req.AssetUnitIds.Any(O => O > 0))
{
predicate = predicate.And(O => req.AssetUnitIds.Contains(O.AssetId));
}
if (req.TradeTypes != null && req.TradeTypes.Any(O => !string.IsNullOrWhiteSpace(O)))
{
predicate = predicate.And(O => req.TradeTypes.Contains(O.TradeType) || req.TradeTypes.Contains(O.StructureType));
}
if (req.UnderlyingIds != null && req.UnderlyingIds.Any(O => O > 0))
{
predicate = predicate.And(O => req.UnderlyingIds.Contains(O.UnderlyingId));
}
if (req.TraderIds != null && req.TraderIds.Any(O => O > 0))
{
predicate = predicate.And(O => req.TraderIds.Contains(O.TraderId));
}
if (req.TradeDateStart > DateTime.MinValue)
{
predicate = predicate.And(O => req.TradeDateStart <= O.TradeDate);
}
if (req.TradeDateEnd > DateTime.MinValue)
{
predicate = predicate.And(O => req.TradeDateEnd >= O.TradeDate);
}
if (req.ExerciseDateStart > DateTime.MinValue)
{
predicate = predicate.And(O => req.ExerciseDateStart <= O.ExerciseDate);
}
if (req.ExerciseDateEnd > DateTime.MinValue)
{
predicate = predicate.And(O => req.ExerciseDateEnd >= O.ExerciseDate);
}
var query = DbContext.trade.Where(predicate);
var tradeInfos = (from diInfo in GetExDividendQuery(req.DividendDate, req.DividendDateEnd.Value)
join t in query
on diInfo.UnderlyingCode equals t.UnderlyingCode
where
t.TradeDate <= diInfo.ExDividendDate &&
t.ValidState != "InValid" &&
(ConsTrade.PositionTradeStatusList.Contains(t.TradeStatus) ||
(ConsTrade.TradeCompleteStatus.Contains(t.TradeStatus) &&
((t.UnWindDate == null && t.ExerciseDate > diInfo.ExDividendDate) ||
(t.UnWindDate != null && t.UnWindDate > diInfo.ExDividendDate))))
select new { diInfo, t })//单标的
.Concat(from diInfo in GetExDividendQuery(req.DividendDate, req.DividendDateEnd.Value)
join ts in DbContext.trade_swap_detail
on diInfo.UnderlyingCode equals ts.UnderlyingCode
join t in query
on ts.TradeId equals t.id
where
t.TradeDate <= diInfo.ExDividendDate &&
t.ValidState != "InValid" &&
(ConsTrade.PositionTradeStatusList.Contains(t.TradeStatus) ||
(ConsTrade.TradeCompleteStatus.Contains(t.TradeStatus) &&
((t.UnWindDate == null && t.ExerciseDate > diInfo.ExDividendDate) ||
(t.UnWindDate != null && t.UnWindDate > diInfo.ExDividendDate))))
select new { diInfo, t })//多标的
.ToList();
//篮子标的
var dividendDict = GetExDividendQuery(req.DividendDate, req.DividendDateEnd.Value).ToDictionary(K => K.UnderlyingCode, V => V);
var umList = DbContext.underlying_manager.Where(O => O.CommodityCode == "篮子标的" && O.SubData != null).ToList();
var codeDict = new Dictionary<string, ex_dividend_info>();
foreach (var item in dividendDict)
{
var ums = umList.Where(O => O.SubData.Contains(item.Key)).ToList();
ums.ForEach(O => codeDict[O.UnderlyingCode.ToLower()] = item.Value);
}
var tempTrade = query.Where(O => codeDict.Keys.Contains(O.UnderlyingCode));
foreach (var t in tempTrade)
{
var code = t.UnderlyingCode.ToLower();
var diInfo = codeDict.ContainsKey(code) ? codeDict[code] : null;
if (diInfo == null)
{
continue;
}
if (t.TradeDate <= diInfo.ExDividendDate && ((t.UnWindDate == null && t.ExerciseDate > diInfo.ExDividendDate) || (t.UnWindDate != null && t.UnWindDate > diInfo.ExDividendDate)))
{
tradeInfos.Add(new { diInfo, t });
}
}
var ids = tradeInfos.Select(O => O.t.id).ToArray();
var subTrades = tradeInfos.Select(O => O.t).ToList();
tradeBLL.SetFieldsByTradeType(subTrades);
var changeService = new DbRecordChangesService<TradeChanges>(this);
var tradeChangeDict = changeService.GetValue(ConsInfoChangeType.Dividend, ids, req.DividendDate).GroupBy(O => O.RecordId).ToDictionary(K => K.Key, V => V.ToList());
var ratioDict = changeService.GetValue(
ConsInfoChangeType.UserChange,
ids,
nameof(trade.DividendRatio),
req.DividendDate).ToDictionary(K => K.RecordId, V =>
{
return double.TryParse(V.NewValue, out var temp) ? (double?)temp : null;
});
foreach (var item in tradeInfos)
{
var info = new DividendTrade
{
TradeId = item.t.id,
EncryptId = item.t.EncryptId,
TradeNumber = item.t.TradeNumber,
ExDividendDate = item.diInfo.ExDividendDate.Value,
DividendStatus = tradeChangeDict.ContainsKey(item.t.id),
TradeStatus = item.t.TradeStatus,
ClientName = item.t.ClientName,
TradeType = item.t.StructureType ?? item.t.TradeType,
TradeDate = item.t.TradeDate.Value,
ExerciseDate = item.t.ExerciseDate.Value,
UnderlyingCode = item.t.UnderlyingCode,
DividendRatio = ratioDict.ContainsKey(item.t.id) ? ratioDict[item.t.id].Value : double.NaN,
trade = item.t
};
var tradeFullInfo = subTrades.Where(O => O.id == item.t.id).FirstOrDefault();
if (info.DividendStatus)
{
changes = tradeChangeDict[item.t.id].Where(O => O.FieldName == nameof(trade.OriginalStockEqvNotional)).FirstOrDefault();
info.StockEqvNotional = TryParseDouble(changes?.NewValue, item.t.OriginalStockEqvNotional ?? double.NaN);
info.StockEqvNotional_After = TryParseDouble(changes?.FieldValue, double.NaN);
changes = tradeChangeDict[item.t.id].Where(O => O.FieldName == nameof(trade.SpotPrice)).FirstOrDefault();
info.SpotPrice = TryParseDouble(changes?.NewValue, info.SpotPrice);
info.SpotPrice_After = TryParseDouble(changes?.FieldValue, double.NaN);
changes = tradeChangeDict[item.t.id].Where(O => O.FieldName == nameof(trade.TradeOriginalAmount)).FirstOrDefault();
info.TradeOriginalAmount = TryParseDouble(changes?.NewValue, info.TradeOriginalAmount);
info.TradeOriginalAmount_After = TryParseDouble(changes?.FieldValue, double.NaN);
if (item.t.IsMoneynessOptionData)
{
var spotPrice = info.SpotPrice_After;
info.Strike = info.SpotPrice * (item.t.Strike ?? 1);
info.Strike_After = spotPrice * (item.t.Strike ?? 1);
switch (item.t.TradeType)
{
case "障碍期权":
info.Barrier = info.SpotPrice * (tradeFullInfo.trade_barrier_option.BarrierPrice ?? 1);
info.Strike = info.SpotPrice;
//info.Strike_After = spotPrice * (tradeFullInfo.trade_barrier_option.BarrierPrice ?? 1);
//info.StrikeHigh = info.SpotPrice * (tradeFullInfo.trade_barrier_option.UpperBarrierPrice ?? 1);
//info.StrikeHigh_After = spotPrice * (tradeFullInfo.trade_barrier_option.UpperBarrierPrice ?? 1);
break;
//case "彩虹期权":
// trade.trade_rainbow_option.SpotPrice1 = NewValue.trade_rainbow_option.SpotPrice1;
// trade.trade_rainbow_option.SpotPrice2 = NewValue.trade_rainbow_option.SpotPrice2;
// trade.trade_rainbow_option.Strike = NewValue.trade_rainbow_option.Strike;
// trade.trade_rainbow_option.Strike2 = NewValue.trade_rainbow_option.Strike2;
// break;
case "凤凰期权":
info.KIBarrier = info.SpotPrice * tradeFullInfo.trade_autocall.KIBarrier;
info.KIBarrier_After = spotPrice * tradeFullInfo.trade_autocall.KIBarrier;
info.KOBarrier = info.SpotPrice * tradeFullInfo.trade_autocall.KOBarrier;
info.KOBarrier_After = spotPrice * tradeFullInfo.trade_autocall.KOBarrier;
info.CouponBarrier = info.SpotPrice * tradeFullInfo.trade_autocall.CouponBarrier;
info.CouponBarrier_After = spotPrice * tradeFullInfo.trade_autocall.CouponBarrier;
info.Strike = info.SpotPrice * (tradeFullInfo.trade_autocall.SpreadStrike ?? 1);
info.Strike_After = spotPrice * (tradeFullInfo.trade_autocall.SpreadStrike ?? 1);
info.StrikeHigh = info.SpotPrice * (tradeFullInfo.trade_autocall.SpreadStrike1 ?? 1);
info.StrikeHigh_After = spotPrice * (tradeFullInfo.trade_autocall.SpreadStrike1 ?? 1);
break;
case "双鲨期权":
info.BarrierHigh = info.SpotPrice * tradeFullInfo.trade_double_sharkfin_option.BarrierHigh;
info.BarrierHigh_After = spotPrice * tradeFullInfo.trade_double_sharkfin_option.BarrierHigh;
info.Barrier = info.SpotPrice * tradeFullInfo.trade_double_sharkfin_option.BarrierLow;
info.Barrier_After = spotPrice * tradeFullInfo.trade_double_sharkfin_option.BarrierLow;
info.StrikeHigh = info.SpotPrice * (tradeFullInfo.trade_double_sharkfin_option.StrikeHigh ?? 1);
info.StrikeHigh_After = spotPrice * (tradeFullInfo.trade_double_sharkfin_option.StrikeHigh ?? 1);
info.Strike = info.SpotPrice * (tradeFullInfo.trade_double_sharkfin_option.StrikeLow ?? 1);
info.Strike_After = spotPrice * (tradeFullInfo.trade_double_sharkfin_option.StrikeLow ?? 1);
break;
case "区间累积期权":
info.LowerRange = info.SpotPrice * tradeFullInfo.trade_rangeaccrual.LowerRange;
info.LowerRange_After = spotPrice * tradeFullInfo.trade_rangeaccrual.LowerRange;
info.UpperRange = info.SpotPrice * tradeFullInfo.trade_rangeaccrual.UpperRange;
info.UpperRange_After = spotPrice * tradeFullInfo.trade_rangeaccrual.UpperRange;
break;
case "雪球期权":
info.KIBarrier = info.SpotPrice * tradeFullInfo.trade_snowball.KIBarrier;
info.KIBarrier_After = spotPrice * tradeFullInfo.trade_snowball.KIBarrier;
info.KOBarrier = info.SpotPrice * tradeFullInfo.trade_snowball.KOBarrier;
info.KOBarrier_After = spotPrice * tradeFullInfo.trade_snowball.KOBarrier;
info.SpreadStrikeAtKO = info.SpotPrice * tradeFullInfo.trade_snowball.SpreadStrikeAtKO;
info.SpreadStrikeAtKO_After = spotPrice * tradeFullInfo.trade_snowball.SpreadStrikeAtKO;
info.SpreadStrikeAtKO1 = info.SpotPrice * tradeFullInfo.trade_snowball.SpreadStrikeAtKO1;
info.SpreadStrikeAtKO1_After = spotPrice * tradeFullInfo.trade_snowball.SpreadStrikeAtKO1;
info.SpreadStrikeAtMaturity = info.SpotPrice * tradeFullInfo.trade_snowball.SpreadStrikeAtMaturity;
info.SpreadStrikeAtMaturity_After = spotPrice * tradeFullInfo.trade_snowball.SpreadStrikeAtMaturity;
info.SpreadStrikeAtMaturity1 = info.SpotPrice * tradeFullInfo.trade_snowball.SpreadStrikeAtMaturity1;
info.SpreadStrikeAtMaturity1_After = spotPrice * tradeFullInfo.trade_snowball.SpreadStrikeAtMaturity1;
break;
case "气囊结构":
info.Barrier = info.SpotPrice * tradeFullInfo.trade_airbag.Barrier;
info.Barrier_After = spotPrice * tradeFullInfo.trade_airbag.Barrier;
info.StrikeHigh = info.SpotPrice * tradeFullInfo.trade_airbag.HighStrike;
info.StrikeHigh_After = spotPrice * tradeFullInfo.trade_airbag.HighStrike;
break;
case "收益互换":
info.PayUnderlyingCode = tradeFullInfo.trade_swap.PayUnderlyingCode;
info.GetUnderlyingCode = tradeFullInfo.trade_swap.GetUnderlyingCode;
break;
}
}
else
{
changes = tradeChangeDict[item.t.id].Where(O => O.FieldName == nameof(trade.Strike)).FirstOrDefault();
info.Strike = TryParseDouble(changes?.NewValue, info.Strike);
info.Strike_After = TryParseDouble(changes?.FieldValue, double.NaN);
var fileName = "";
switch (item.t.TradeType)
{
case "障碍期权":
fileName = nameof(trade.trade_barrier_option) + "." + nameof(trade_barrier_option.BarrierPrice);
changes = tradeChangeDict[item.t.id].Where(O => O.FieldName == fileName).FirstOrDefault();
info.Strike = TryParseDouble(changes?.NewValue, info.Strike);
info.Strike_After = TryParseDouble(changes?.FieldValue, double.NaN);
fileName = nameof(trade.trade_barrier_option) + "." + nameof(trade_barrier_option.UpperBarrierPrice);
changes = tradeChangeDict[item.t.id].Where(O => O.FieldName == fileName).FirstOrDefault();
info.StrikeHigh = TryParseDouble(changes?.NewValue, info.StrikeHigh);
info.StrikeHigh_After = TryParseDouble(changes?.FieldValue, double.NaN);
break;
//case "彩虹期权":
// trade.trade_rainbow_option.SpotPrice1 = NewValue.trade_rainbow_option.SpotPrice1;
// trade.trade_rainbow_option.SpotPrice2 = NewValue.trade_rainbow_option.SpotPrice2;
// trade.trade_rainbow_option.Strike = NewValue.trade_rainbow_option.Strike;
// trade.trade_rainbow_option.Strike2 = NewValue.trade_rainbow_option.Strike2;
// break;
case "凤凰期权":
fileName = nameof(trade.trade_autocall) + "." + nameof(trade_autocall.KIBarrier);
changes = tradeChangeDict[item.t.id].Where(O => O.FieldName == fileName).FirstOrDefault();
info.KIBarrier = TryParseDouble(changes?.NewValue, info.KIBarrier);
info.KIBarrier_After = TryParseDouble(changes?.FieldValue, double.NaN);
fileName = nameof(trade.trade_autocall) + "." + nameof(trade_autocall.KOBarrier);
changes = tradeChangeDict[item.t.id].Where(O => O.FieldName == fileName).FirstOrDefault();
info.KOBarrier = TryParseDouble(changes?.NewValue, info.KOBarrier);
info.KOBarrier_After = TryParseDouble(changes?.FieldValue, double.NaN);
fileName = nameof(trade.trade_autocall) + "." + nameof(trade_autocall.CouponBarrier);
changes = tradeChangeDict[item.t.id].Where(O => O.FieldName == fileName).FirstOrDefault();
info.CouponBarrier = TryParseDouble(changes?.NewValue, info.CouponBarrier);
info.CouponBarrier_After = TryParseDouble(changes?.FieldValue, double.NaN);
fileName = nameof(trade.trade_autocall) + "." + nameof(trade_autocall.SpreadStrike);
changes = tradeChangeDict[item.t.id].Where(O => O.FieldName == fileName).FirstOrDefault();
info.Strike = TryParseDouble(changes?.NewValue, info.Strike);
info.Strike_After = TryParseDouble(changes?.FieldValue, double.NaN);
fileName = nameof(trade.trade_autocall) + "." + nameof(trade_autocall.SpreadStrike1);
changes = tradeChangeDict[item.t.id].Where(O => O.FieldName == fileName).FirstOrDefault();
info.StrikeHigh = TryParseDouble(changes?.NewValue, info.StrikeHigh);
info.StrikeHigh_After = TryParseDouble(changes?.FieldValue, double.NaN);
break;
case "双鲨期权":
fileName = nameof(trade.trade_double_sharkfin_option) + "." + nameof(trade_double_sharkfin_option.BarrierHigh);
changes = tradeChangeDict[item.t.id].Where(O => O.FieldName == fileName).FirstOrDefault();
info.BarrierHigh = TryParseDouble(changes?.NewValue, info.BarrierHigh);
info.BarrierHigh_After = TryParseDouble(changes?.FieldValue, double.NaN);
fileName = nameof(trade.trade_double_sharkfin_option) + "." + nameof(trade_double_sharkfin_option.BarrierLow);
changes = tradeChangeDict[item.t.id].Where(O => O.FieldName == fileName).FirstOrDefault();
info.Barrier = TryParseDouble(changes?.NewValue, info.Barrier);
info.Barrier_After = TryParseDouble(changes?.FieldValue, double.NaN);
fileName = nameof(trade.trade_double_sharkfin_option) + "." + nameof(trade_double_sharkfin_option.StrikeHigh);
changes = tradeChangeDict[item.t.id].Where(O => O.FieldName == fileName).FirstOrDefault();
info.StrikeHigh = TryParseDouble(changes?.NewValue, info.StrikeHigh);
info.StrikeHigh_After = TryParseDouble(changes?.FieldValue, double.NaN);
fileName = nameof(trade.trade_double_sharkfin_option) + "." + nameof(trade_double_sharkfin_option.StrikeLow);
changes = tradeChangeDict[item.t.id].Where(O => O.FieldName == fileName).FirstOrDefault();
info.Strike = TryParseDouble(changes?.NewValue, info.Strike);
info.Strike_After = TryParseDouble(changes?.FieldValue, double.NaN);
break;
case "区间累积期权":
fileName = nameof(trade.trade_rangeaccrual) + "." + nameof(trade_rangeaccrual.LowerRange);
changes = tradeChangeDict[item.t.id].Where(O => O.FieldName == fileName).FirstOrDefault();
info.LowerRange = TryParseDouble(changes?.NewValue, info.LowerRange);
info.LowerRange_After = TryParseDouble(changes?.FieldValue, double.NaN);
fileName = nameof(trade.trade_rangeaccrual) + "." + nameof(trade_rangeaccrual.UpperRange);
changes = tradeChangeDict[item.t.id].Where(O => O.FieldName == fileName).FirstOrDefault();
info.UpperRange = TryParseDouble(changes?.NewValue, info.UpperRange);
info.UpperRange_After = TryParseDouble(changes?.FieldValue, double.NaN);
break;
case "雪球期权":
fileName = nameof(trade.trade_snowball) + "." + nameof(trade_snowball.KIBarrier);
changes = tradeChangeDict[item.t.id].Where(O => O.FieldName == fileName).FirstOrDefault();
info.KIBarrier = TryParseDouble(changes?.NewValue, info.KIBarrier);
info.KIBarrier_After = TryParseDouble(changes?.FieldValue, double.NaN);
fileName = nameof(trade.trade_snowball) + "." + nameof(trade_snowball.KOBarrier);
changes = tradeChangeDict[item.t.id].Where(O => O.FieldName == fileName).FirstOrDefault();
info.KOBarrier = TryParseDouble(changes?.NewValue, info.KOBarrier);
info.KOBarrier_After = TryParseDouble(changes?.FieldValue, double.NaN);
fileName = nameof(trade.trade_snowball) + "." + nameof(trade_snowball.SpreadStrikeAtKO);
changes = tradeChangeDict[item.t.id].Where(O => O.FieldName == fileName).FirstOrDefault();
info.SpreadStrikeAtKO = TryParseDouble(changes?.NewValue, info.SpreadStrikeAtKO ?? double.NaN);
info.SpreadStrikeAtKO_After = TryParseDouble(changes?.FieldValue, double.NaN);
fileName = nameof(trade.trade_snowball) + "." + nameof(trade_snowball.SpreadStrikeAtKO1);
changes = tradeChangeDict[item.t.id].Where(O => O.FieldName == fileName).FirstOrDefault();
info.SpreadStrikeAtKO1 = TryParseDouble(changes?.NewValue, info.SpreadStrikeAtKO1 ?? double.NaN);
info.SpreadStrikeAtKO1_After = TryParseDouble(changes?.FieldValue, double.NaN);
fileName = nameof(trade.trade_snowball) + "." + nameof(trade_snowball.SpreadStrikeAtMaturity);
changes = tradeChangeDict[item.t.id].Where(O => O.FieldName == fileName).FirstOrDefault();
info.SpreadStrikeAtMaturity = TryParseDouble(changes?.NewValue, info.SpreadStrikeAtMaturity ?? double.NaN);
info.SpreadStrikeAtMaturity_After = TryParseDouble(changes?.FieldValue, double.NaN);
fileName = nameof(trade.trade_snowball) + "." + nameof(trade_snowball.SpreadStrikeAtMaturity1);
changes = tradeChangeDict[item.t.id].Where(O => O.FieldName == fileName).FirstOrDefault();
info.SpreadStrikeAtMaturity1 = TryParseDouble(changes?.NewValue, info.SpreadStrikeAtMaturity1 ?? double.NaN);
info.SpreadStrikeAtMaturity1_After = TryParseDouble(changes?.FieldValue, double.NaN);
break;
case "气囊结构":
fileName = nameof(trade.trade_airbag) + "." + nameof(trade_airbag.Barrier);
changes = tradeChangeDict[item.t.id].Where(O => O.FieldName == fileName).FirstOrDefault();
info.Barrier = TryParseDouble(changes?.NewValue, info.Barrier);
info.Barrier_After = TryParseDouble(changes?.FieldValue, double.NaN);
fileName = nameof(trade.trade_airbag) + "." + nameof(trade_airbag.HighStrike);
changes = tradeChangeDict[item.t.id].Where(O => O.FieldName == fileName).FirstOrDefault();
info.StrikeHigh = TryParseDouble(changes?.NewValue, info.StrikeHigh);
info.StrikeHigh_After = TryParseDouble(changes?.FieldValue, double.NaN);
break;
case "收益互换":
info.PayUnderlyingCode = tradeFullInfo.trade_swap.PayUnderlyingCode;
info.GetUnderlyingCode = tradeFullInfo.trade_swap.GetUnderlyingCode;
fileName = nameof(trade.trade_swap) + "." + nameof(trade_swap.PaySpotPrice);
changes = tradeChangeDict[item.t.id].Where(O => O.FieldName == fileName).FirstOrDefault();
info.PaySpotPrice = TryParseDouble(changes?.NewValue, info.PaySpotPrice);
info.PaySpotPrice_After = TryParseDouble(changes?.FieldValue, double.NaN);
fileName = nameof(trade.trade_swap) + "." + nameof(trade_swap.GetSpotPrice);
changes = tradeChangeDict[item.t.id].Where(O => O.FieldName == fileName).FirstOrDefault();
info.GetSpotPrice = TryParseDouble(changes?.NewValue, info.GetSpotPrice);
info.GetSpotPrice_After = TryParseDouble(changes?.FieldValue, double.NaN);
break;
case "累计期权":
throw new NotImplementedException("QueryDividendTrade中未实现累计期权逻辑");
}
}
}
else
{
info.StockEqvNotional = item.t.OriginalStockEqvNotional ?? double.NaN;
info.StockEqvNotional_After = double.NaN;
info.SpotPrice = item.t.SpotPrice ?? double.NaN;
info.SpotPrice_After = double.NaN;
info.TradeOriginalAmount = item.t.TradeOriginalAmount ?? item.t.OriginalNotional ?? 0;
info.TradeOriginalAmount_After = double.NaN;
if (item.t.IsMoneynessOptionData)
{
info.Strike = info.SpotPrice * (item.t.Strike ?? 1);
info.Strike_After = double.NaN;
switch (item.t.TradeType)
{
case "障碍期权":
info.Strike = info.SpotPrice * (tradeFullInfo.trade_barrier_option.BarrierPrice ?? 1);
info.Strike_After = double.NaN;
info.StrikeHigh = info.SpotPrice * (tradeFullInfo.trade_barrier_option.UpperBarrierPrice ?? 1);
info.StrikeHigh_After = double.NaN;
break;
//case "彩虹期权":
// trade.trade_rainbow_option.SpotPrice1 = NewValue.trade_rainbow_option.SpotPrice1;
// trade.trade_rainbow_option.SpotPrice2 = NewValue.trade_rainbow_option.SpotPrice2;
// trade.trade_rainbow_option.Strike = NewValue.trade_rainbow_option.Strike;
// trade.trade_rainbow_option.Strike2 = NewValue.trade_rainbow_option.Strike2;
// break;
case "凤凰期权":
info.KIBarrier = info.SpotPrice * tradeFullInfo.trade_autocall.KIBarrier;
info.KIBarrier_After = double.NaN;
info.KOBarrier = info.SpotPrice * tradeFullInfo.trade_autocall.KOBarrier;
info.KOBarrier_After = double.NaN;
info.CouponBarrier = info.SpotPrice * tradeFullInfo.trade_autocall.CouponBarrier;
info.CouponBarrier_After = double.NaN;
info.Strike = info.SpotPrice * (tradeFullInfo.trade_autocall.SpreadStrike ?? 1);
info.Strike_After = double.NaN;
info.StrikeHigh = info.SpotPrice * (tradeFullInfo.trade_autocall.SpreadStrike1 ?? 1);
info.StrikeHigh_After = double.NaN;
break;
case "双鲨期权":
info.BarrierHigh = info.SpotPrice * tradeFullInfo.trade_double_sharkfin_option.BarrierHigh;
info.BarrierHigh_After = double.NaN;
info.Barrier = info.SpotPrice * tradeFullInfo.trade_double_sharkfin_option.BarrierLow;
info.Barrier_After = double.NaN;
info.StrikeHigh = info.SpotPrice * (tradeFullInfo.trade_double_sharkfin_option.StrikeHigh ?? 1);
info.StrikeHigh_After = double.NaN;
info.Strike = info.SpotPrice * (tradeFullInfo.trade_double_sharkfin_option.StrikeLow ?? 1);
info.Strike_After = double.NaN;
break;
case "区间累积期权":
info.LowerRange = info.SpotPrice * tradeFullInfo.trade_rangeaccrual.LowerRange;
info.LowerRange_After = double.NaN;
info.UpperRange = info.SpotPrice * tradeFullInfo.trade_rangeaccrual.UpperRange;
info.UpperRange_After = double.NaN;
break;
case "雪球期权":
info.KIBarrier = info.SpotPrice * tradeFullInfo.trade_snowball.KIBarrier;
info.KIBarrier_After = double.NaN;
info.KOBarrier = info.SpotPrice * tradeFullInfo.trade_snowball.KOBarrier;
info.KOBarrier_After = double.NaN;
info.SpreadStrikeAtKO = info.SpotPrice * tradeFullInfo.trade_snowball.SpreadStrikeAtKO;
info.SpreadStrikeAtKO_After = double.NaN;
info.SpreadStrikeAtKO1 = info.SpotPrice * tradeFullInfo.trade_snowball.SpreadStrikeAtKO1;
info.SpreadStrikeAtKO1_After = double.NaN;
info.SpreadStrikeAtMaturity = info.SpotPrice * tradeFullInfo.trade_snowball.SpreadStrikeAtMaturity;
info.SpreadStrikeAtMaturity_After = double.NaN;
info.SpreadStrikeAtMaturity1 = info.SpotPrice * tradeFullInfo.trade_snowball.SpreadStrikeAtMaturity1;
info.SpreadStrikeAtMaturity1_After = double.NaN;
break;
case "气囊结构":
info.Barrier = info.SpotPrice * tradeFullInfo.trade_airbag.Barrier;
info.Barrier_After = double.NaN;
info.StrikeHigh = info.SpotPrice * tradeFullInfo.trade_airbag.HighStrike;
info.StrikeHigh_After = double.NaN;
break;
case "收益互换":
info.PayUnderlyingCode = tradeFullInfo.trade_swap.PayUnderlyingCode;
info.GetUnderlyingCode = tradeFullInfo.trade_swap.GetUnderlyingCode;
break;
}
}
else
{
info.Strike = item.t.Strike ?? double.NaN;
info.Strike_After = double.NaN;
switch (item.t.TradeType)
{
case "障碍期权":
info.Barrier = item.t.trade_barrier_option.BarrierPrice ?? 1;
info.Barrier_After = double.NaN;
info.BarrierHigh = item.t.trade_barrier_option.UpperBarrierPrice ?? 1;
info.BarrierHigh_After = double.NaN;
break;
//case "彩虹期权":
// trade.trade_rainbow_option.SpotPrice1 = NewValue.trade_rainbow_option.SpotPrice1;
// trade.trade_rainbow_option.SpotPrice2 = NewValue.trade_rainbow_option.SpotPrice2;
// trade.trade_rainbow_option.Strike = NewValue.trade_rainbow_option.Strike;
// trade.trade_rainbow_option.Strike2 = NewValue.trade_rainbow_option.Strike2;
// break;
case "凤凰期权":
info.KIBarrier = item.t.trade_autocall.KIBarrier;
info.KIBarrier_After = double.NaN;
info.KOBarrier = item.t.trade_autocall.KOBarrier;
info.KOBarrier_After = double.NaN;
info.CouponBarrier = item.t.trade_autocall.CouponBarrier;
info.CouponBarrier_After = double.NaN;
info.Strike = item.t.trade_autocall.SpreadStrike ?? 1;
info.Strike_After = double.NaN;
info.StrikeHigh = item.t.trade_autocall.SpreadStrike1 ?? 1;
info.StrikeHigh_After = double.NaN;
break;
case "双鲨期权":
info.BarrierHigh = item.t.trade_double_sharkfin_option.BarrierHigh;
info.BarrierHigh_After = double.NaN;
info.Barrier = item.t.trade_double_sharkfin_option.BarrierLow;
info.Barrier_After = double.NaN;
info.StrikeHigh = item.t.trade_double_sharkfin_option.StrikeHigh ?? 0;
info.StrikeHigh_After = double.NaN;
info.Strike = item.t.trade_double_sharkfin_option.StrikeLow ?? 0;
info.Strike_After = double.NaN;
break;
case "区间累积期权":
info.LowerRange = item.t.trade_rangeaccrual.LowerRange;
info.LowerRange_After = double.NaN;
info.UpperRange = item.t.trade_rangeaccrual.UpperRange;
info.UpperRange_After = double.NaN;
break;
case "雪球期权":
info.KIBarrier = item.t.trade_snowball.KIBarrier;
info.KIBarrier_After = double.NaN;
info.KOBarrier = item.t.trade_snowball.KOBarrier;
info.KOBarrier_After = double.NaN;
info.SpreadStrikeAtKO = info.SpotPrice * item.t.trade_snowball.SpreadStrikeAtKO;
info.SpreadStrikeAtKO_After = double.NaN;
info.SpreadStrikeAtKO1 = info.SpotPrice * item.t.trade_snowball.SpreadStrikeAtKO1;
info.SpreadStrikeAtKO1_After = double.NaN;
info.SpreadStrikeAtMaturity = info.SpotPrice * item.t.trade_snowball.SpreadStrikeAtMaturity;
info.SpreadStrikeAtMaturity_After = double.NaN;
info.SpreadStrikeAtMaturity1 = info.SpotPrice * item.t.trade_snowball.SpreadStrikeAtMaturity1;
info.SpreadStrikeAtMaturity1_After = double.NaN;
break;
case "气囊结构":
info.Barrier = item.t.trade_airbag.Barrier;
info.Barrier_After = double.NaN;
info.StrikeHigh = item.t.trade_airbag.HighStrike;
info.StrikeHigh_After = double.NaN;
break;
case "收益互换":
info.PayUnderlyingCode = tradeFullInfo.trade_swap.PayUnderlyingCode;
info.GetUnderlyingCode = tradeFullInfo.trade_swap.GetUnderlyingCode;
info.PaySpotPrice = item.t.trade_swap.PaySpotPrice ?? double.NaN;
info.PaySpotPrice_After = double.NaN;
info.GetSpotPrice = item.t.trade_swap.GetSpotPrice ?? double.NaN;
info.GetSpotPrice_After = double.NaN;
break;
}
}
}
info.TradeOriginalAmountV = CalculationModule.TradeCalcHelper.GetTradeAmountV(item.t, info.TradeOriginalAmount, 1);
info.TradeOriginalAmountV_After = CalculationModule.TradeCalcHelper.GetTradeAmountV(item.t, info.TradeOriginalAmount_After, 1);
result.Add(info);
}
if (req.DividendStatus != null)
{
result = result.Where(O => O.DividendStatus == req.DividendStatus.Value).ToList();
}
return result;
}
private double TryParseDouble(string str, double defaultValue)
{
return double.TryParse(str, out var result) ? result : defaultValue;
}
public byte[] ExportDividendTrade(DividendTradeReq req)
{
var ret = QueryDividendTrade(req);
var dc = new List<ExcelHelper.DataColumnModel>
{
new ExcelHelper.DataColumnModel("交易编号", "TradeNumber"),
new ExcelHelper.DataColumnModel("股权登记日", "ExDividendDate", dateFormatter),
new ExcelHelper.DataColumnModel("除权除息状态", "DividendStatus", (cv, obj) =>
{
var status = (bool)cv;
return status ? "已除权" : "未除权";
}),
new ExcelHelper.DataColumnModel("交易状态", "TradeStatus"),
new ExcelHelper.DataColumnModel("交易对手方", "ClientName"),
new ExcelHelper.DataColumnModel("结构类型", "TradeType"),
new ExcelHelper.DataColumnModel("名义本金", "StockEqvNotional", (cv, obj) =>
{
var result = "";
if (obj is DividendTrade trade)
{
result = priceFormatter(trade.StockEqvNotional, trade.StockEqvNotional_After, OtcFormatFlag.umprice);
}
return result;
}),
new ExcelHelper.DataColumnModel("成交日期", "TradeDate", dateFormatter),
new ExcelHelper.DataColumnModel("到期日期", "ExerciseDate", dateFormatter),
new ExcelHelper.DataColumnModel("标的代码", "UnderlyingCode"),
new ExcelHelper.DataColumnModel("除权除息系数", "DividendRatio", (cv, obj) =>
{
var result = "";
if (obj is DividendTrade trade)
{
result = priceFormatter(trade.DividendRatio, double.NaN, 6);
}
return result;
}),
new ExcelHelper.DataColumnModel("期初标的价格", "SpotPrice", (cv, obj) =>
{
var result = "";
if (obj is DividendTrade trade)
{
result = priceFormatter(trade.SpotPrice, trade.SpotPrice_After, OtcFormatFlag.umprice);
}
return result;
}),
new ExcelHelper.DataColumnModel("执行价格", "Strike", (cv, obj) =>
{
var result = "";
if (obj is DividendTrade trade)
{
result = priceFormatter(trade.Strike, trade.Strike_After, OtcFormatFlag.umprice);
}
return result;
}),
new ExcelHelper.DataColumnModel("成交数量", "TradeOriginalAmount", (cv, obj) =>
{
var result = "";
if (obj is DividendTrade trade)
{
var amountv = CalculationModule.TradeCalcHelper.GetTradeAmountV(trade.trade,trade.TradeOriginalAmount,1);
var amountv_after = CalculationModule.TradeCalcHelper.GetTradeAmountV(trade.trade,trade.TradeOriginalAmount_After,1);
result = priceFormatter(amountv, amountv_after, OtcFormatFlag.notional);
}
return result;
}),
new ExcelHelper.DataColumnModel("有效成交数量", "TradeOriginalAmount", (cv, obj) =>
{
var result = "";
if (obj is DividendTrade trade)
{
result = priceFormatter(trade.TradeOriginalAmount, trade.TradeOriginalAmount_After, OtcFormatFlag.notional);
}
return result;
}),
new ExcelHelper.DataColumnModel("高行权价", "StrikeHigh", (cv, obj) =>
{
var result = "";
if (obj is DividendTrade trade)
{
result = priceFormatter(trade.StrikeHigh, trade.StrikeHigh_After, OtcFormatFlag.umprice);
}
return result;
}),
new ExcelHelper.DataColumnModel("障碍价格", "Barrier", (cv, obj) =>
{
var result = "";
if (obj is DividendTrade trade)
{
result = priceFormatter(trade.Barrier, trade.Barrier_After, OtcFormatFlag.umprice);
}
return result;
}),
new ExcelHelper.DataColumnModel("高障碍价格", "BarrierHigh", (cv, obj) =>
{
var result = "";
if (obj is DividendTrade trade)
{
result = priceFormatter(trade.BarrierHigh, trade.BarrierHigh_After, OtcFormatFlag.umprice);
}
return result;
}),
new ExcelHelper.DataColumnModel("敲入障碍价格", "KIBarrier", (cv, obj) =>
{
var result = "";
if (obj is DividendTrade trade)
{
result = priceFormatter(trade.KIBarrier, trade.KIBarrier_After, OtcFormatFlag.umprice);
}
return result;
}),
new ExcelHelper.DataColumnModel("敲出障碍价格", "KOBarrier", (cv, obj) =>
{
var result = "";
if (obj is DividendTrade trade)
{
result = priceFormatter(trade.KOBarrier, trade.KOBarrier_After, OtcFormatFlag.umprice);
}
return result;
}),
new ExcelHelper.DataColumnModel("票息障碍价格", "CouponBarrier", (cv, obj) =>
{
var result = "";
if (obj is DividendTrade trade)
{
result = priceFormatter(trade.CouponBarrier, trade.CouponBarrier_After, OtcFormatFlag.umprice);
}
return result;
}),
new ExcelHelper.DataColumnModel("互换标的代码(收取方)", "GetUnderlyingCode"),
new ExcelHelper.DataColumnModel("互换标的价格(收取方)", "GetSpotPrice", (cv, obj) =>
{
var result = "";
if (obj is DividendTrade trade)
{
result = priceFormatter(trade.GetSpotPrice, trade.GetSpotPrice_After, OtcFormatFlag.umprice);
}
return result;
}),
new ExcelHelper.DataColumnModel("互换标的代码(支付方)", "PayUnderlyingCode"),
new ExcelHelper.DataColumnModel("互换标的价格(支付方)", "PaySpotPrice", (cv, obj) =>
{
var result = "";
if (obj is DividendTrade trade)
{
result = priceFormatter(trade.PaySpotPrice, trade.PaySpotPrice_After, OtcFormatFlag.umprice);
}
return result;
}),
new ExcelHelper.DataColumnModel("区间下限", "LowerRange", (cv, obj) =>
{
var result = "";
if (obj is DividendTrade trade)
{
result = priceFormatter(trade.LowerRange, trade.LowerRange_After, OtcFormatFlag.umprice);
}
return result;
}),
new ExcelHelper.DataColumnModel("区间上限", "UpperRange", (cv, obj) =>
{
var result = "";
if (obj is DividendTrade trade)
{
result = priceFormatter(trade.UpperRange, trade.UpperRange_After, OtcFormatFlag.umprice);
}
return result;
}),
new ExcelHelper.DataColumnModel("敲出期权行权价1", "SpreadStrikeAtKO1", (cv, obj) =>
{
var result = "";
if (obj is DividendTrade trade)
{
result = priceFormatter(trade.SpreadStrikeAtKO1 ?? double.NaN, trade.SpreadStrikeAtKO1_After ?? double.NaN, OtcFormatFlag.umprice);
}
return result;
}),
new ExcelHelper.DataColumnModel("敲出期权行权价2", "SpreadStrikeAtKO", (cv, obj) =>
{
var result = "";
if (obj is DividendTrade trade)
{
result = priceFormatter(trade.SpreadStrikeAtKO ?? double.NaN, trade.SpreadStrikeAtKO_After ?? double.NaN, OtcFormatFlag.umprice);
}
return result;
}),
new ExcelHelper.DataColumnModel("敲入期权行权价1", "SpreadStrikeAtMaturity1", (cv, obj) =>
{
var result = "";
if (obj is DividendTrade trade)
{
result = priceFormatter(trade.SpreadStrikeAtMaturity1 ?? double.NaN, trade.SpreadStrikeAtMaturity1_After ?? double.NaN, OtcFormatFlag.umprice);
}
return result;
}),
new ExcelHelper.DataColumnModel("封顶/封底行权价", "SpreadStrikeAtMaturity", (cv, obj) =>
{
var result = "";
if (obj is DividendTrade trade)
{
result = priceFormatter(trade.SpreadStrikeAtMaturity ?? double.NaN, trade.SpreadStrikeAtMaturity_After ?? double.NaN, OtcFormatFlag.umprice);
}
return result;
})
};
new ExcelHelper().ListToExcel<DividendTrade>(dc.ToArray(), ret, "Sheet1", true, out var buffer);
return buffer;
}
private string dateFormatter(dynamic cv, object obj)
{
var result = "";
if (cv is DateTime dt)
{
result = dt.ToString("yyyy-MM-dd");
}
return result;
}
private string priceFormatter(double p1, double p2, int precision)
{
var result = "";
if (!double.IsNaN(p1) && p1 != 0)
{
result = OtcFormatExtensions.OtcFormatFlex(p1, minDecimals: precision, maxDecimals: precision);
}
if (!double.IsNaN(p2) && p2 != 0)
{
result += $"({OtcFormatExtensions.OtcFormatFlex(p2, minDecimals: precision, maxDecimals: precision)})";
}
return result;
}
private string priceFormatter(double p1, double p2, OtcFormatFlag formatFlag)
{
var result = "";
if (!double.IsNaN(p1) && p1 != 0)
{
result = OtcFormatExtensions.OtcFormat(p1, formatFlag);
}
if (!double.IsNaN(p2) && p2 != 0)
{
result += $"({OtcFormatExtensions.OtcFormat(p2, formatFlag)})";
}
return result;
}
}
}