339 lines
16 KiB
C#
339 lines
16 KiB
C#
using System.Linq.Expressions;
|
|
using YLErp.BLL;
|
|
using YLErp.Modules.DataProviderModule;
|
|
using YLErp.Modules.TradeModule;
|
|
using YLErp.Modules.TradeModule.ForwardModule;
|
|
|
|
namespace YLErp.Modules.EodModule.SettlementModule
|
|
{
|
|
/// <summary>
|
|
/// 用于结算的数据查询服务
|
|
/// </summary>
|
|
public class EodSettleDataQueryService : YLBaseService
|
|
{
|
|
public EodSettleDataQueryService(OptUserInfo optUser) : base(optUser)
|
|
{
|
|
|
|
}
|
|
|
|
public EodSettleDataQueryService(YLBaseService baseService) : base(baseService)
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// 获取要结算的交易数据
|
|
/// </summary>
|
|
/// <param name="settleDate">结算日期</param>
|
|
/// <param name="tradeDataPredicate">交易数据查询过滤条件</param>
|
|
/// <param name="tradecashProvider">交易资金提供接口</param>
|
|
/// <param name="preSettleDate">上个结算日(多交易日历场景下的假日到期交易处理时使用)</param>
|
|
public List<trade> GetOtcTrades(DateTime settleDate, Expression<Func<trade, bool>> tradeDataPredicate
|
|
, out TradeCashDataProvider tradecashProvider, DateTime preSettleDate = default)
|
|
{
|
|
if (tradeDataPredicate is null)
|
|
{
|
|
throw new ArgumentNullException(nameof(tradeDataPredicate));
|
|
}
|
|
|
|
var tradeList = new List<trade>(1000);
|
|
|
|
//------------------------------------------------------
|
|
// eod trades
|
|
//------------------------------------------------------
|
|
|
|
var etQuery = from et in DbContext.eod_trade
|
|
join t in DbContext.trade.Where(tradeDataPredicate) on et.TradeId equals t.id
|
|
where et.ValueDate == settleDate
|
|
select new
|
|
{
|
|
t.id,
|
|
et.TradeId,
|
|
et.TradeJson,
|
|
t.BuySell,
|
|
t.AssetId,
|
|
t.ExerciseDate,
|
|
t.DividendRate,
|
|
t.TradeType
|
|
};
|
|
|
|
var etList = etQuery.ToList();
|
|
List<TradeMeta> structProductTradeMetaList = null;
|
|
if (etList != null && etList.Count() > 0)
|
|
{
|
|
var structProductTradeIds = etList.Where(p => "结构化产品".Equals(p.TradeType)).Select(p => p.id).Distinct().ToList();
|
|
if (structProductTradeIds != null && structProductTradeIds.Count > 0)
|
|
{
|
|
structProductTradeMetaList = DbContext.TradeMeta.AsNoTracking().Where(d => structProductTradeIds.Contains(d.TradeId)).ToList();
|
|
}
|
|
}
|
|
if (structProductTradeMetaList == null)
|
|
{
|
|
structProductTradeMetaList = new List<TradeMeta>();
|
|
}
|
|
|
|
|
|
foreach (var item in etList)
|
|
{
|
|
try
|
|
{
|
|
var t = TradeHelper2.Deserialize(item.TradeJson);
|
|
t.id = item.TradeId;
|
|
t.BuySell = item.BuySell;
|
|
t.AssetId = item.AssetId;
|
|
if (t.ExerciseDate != item.ExerciseDate && !DbContext.ExtensionTime.Any(x => x.TradeId == t.id && x.IsValid && x.OldMaturityDate == t.ExerciseDate && x.NewMaturityDate == item.ExerciseDate && x.OptDate >= settleDate))
|
|
{
|
|
t.ExerciseDate = item.ExerciseDate;
|
|
}
|
|
t.MetaDic["from_eod_trade"] = "true";
|
|
tradeList.Add(t);
|
|
//先简单处理一下避免某些情况下删除eodtrade表数据
|
|
//比如手动在数据库改了观察频率回到历史收盘(手动改是因为回到历史收盘太慢了而且涉及到除权除息的各种问题)
|
|
if (t.TradeType == "雪球期权")
|
|
{
|
|
var snowball = DbContext.trade_snowball.Where(n => n.TradeId == item.id).Select(n => new
|
|
{
|
|
n.KOObservationDates,
|
|
n.ObservationDates,
|
|
n.KOObservationSettleDates
|
|
}).FirstOrDefault();
|
|
|
|
if (snowball != null && t.trade_snowball != null)
|
|
{
|
|
t.trade_snowball.ObservationDates = snowball.ObservationDates;
|
|
t.trade_snowball.KOObservationDates = snowball.KOObservationDates;
|
|
t.trade_snowball.KOObservationSettleDates = snowball.KOObservationSettleDates;
|
|
}
|
|
}
|
|
else if (t.TradeType == "凤凰期权")
|
|
{
|
|
var autocall = DbContext.trade_autocall.Where(n => n.TradeId == item.id).Select(n => new
|
|
{
|
|
n.ObservationDates,
|
|
n.KOObservationDates
|
|
}).FirstOrDefault();
|
|
if (autocall != null && t.trade_autocall != null)
|
|
{
|
|
t.trade_autocall.ObservationDates = autocall.ObservationDates;
|
|
t.trade_autocall.KOObservationDates = autocall.KOObservationDates;
|
|
}
|
|
}
|
|
else if (t.TradeType == "累计期权")
|
|
{
|
|
t.MetaDic.Remove("from_eod_trade");
|
|
new OptionTradeActionRestoreService(this).RestoreTradeDataToSpecialDay(t, settleDate);
|
|
}
|
|
else if (t.TradeType == "远期")
|
|
{
|
|
var forwardTrade = DbContext.trade_forward.Where(n => n.TradeId == item.id).Select(n => new
|
|
{
|
|
n.ForwardValue,
|
|
n.OpenCommission
|
|
}).FirstOrDefault();
|
|
}
|
|
else if (t.TradeType == "结构化产品")
|
|
{
|
|
var tradeMetas = structProductTradeMetaList.Where(d => d.TradeId == t.id).ToDictionary(g => g.MetaKey, g => g.MetaValue);
|
|
if (tradeMetas != null && tradeMetas.Count > 0)
|
|
{
|
|
foreach (var d in tradeMetas)
|
|
{
|
|
if (!t.MetaDic.ContainsKey(d.Key))
|
|
{
|
|
t.MetaDic.Add(d.Key, d.Value);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (t.TradeType == "亚式期权")
|
|
{
|
|
if (t.trade_asian_option.PayoffType == "EnhancedArithmeticAverage" && t.trade_asian_option.EnhancedPrice == 0 && t.TradeDate < new DateTime(2023, 2, 19) && t.Strike.HasValue)
|
|
{
|
|
t.trade_asian_option.EnhancedPrice = t.Strike.Value;
|
|
}
|
|
}
|
|
|
|
//解决收盘时从hisdata取了null并且存入了eodtrade的情况下数据问题
|
|
if (t.DividendRate == null)
|
|
{
|
|
t.DividendRate = item.DividendRate;
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
LogFactory.GetLogger("eod_trade.TradeJson解析").Error(ex);
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------
|
|
// 多交易日历场景下的假日到期交易处理
|
|
//------------------------------------------------------
|
|
if (PS.Config.ErpElement.SupportMultiCalendar)
|
|
{
|
|
var pretQuery = from et in DbContext.eod_trade
|
|
join t in DbContext.trade on et.TradeId equals t.id
|
|
where et.ValueDate == preSettleDate
|
|
&& !DbContext.trade.Where(tradeDataPredicate).Any(n => n.id == et.TradeId)
|
|
select new
|
|
{
|
|
et.TradeId,
|
|
et.TradeJson,
|
|
t.BuySell,
|
|
t.UnderlyingCode
|
|
};
|
|
|
|
var pretList = pretQuery.ToArray();
|
|
|
|
if (pretList.Any())
|
|
{
|
|
var exHolidaySet = DataCacheProvider.GetMarketDataSource().AsQueryable()
|
|
.Where(n => !string.IsNullOrEmpty(n.ExchangeNo) && QdpModule.QdpCalendarHelper.IsHoliday(preSettleDate, n.CalendarName.TrimToNull() ?? ConsGlobal.DefaultCalendar))
|
|
.Select(n => n.ExchangeNo.ToUpperInvariant()).ToHashSet();
|
|
|
|
foreach (var item in pretList)
|
|
{
|
|
if (!tradeList.Any(x => x.id == item.TradeId))
|
|
{
|
|
var un = DataCacheProvider.GetUnderlyingDataSource().GetData(item.UnderlyingCode);
|
|
|
|
if (!string.IsNullOrEmpty(un.MarketCode) && exHolidaySet.Contains(un.MarketCode))
|
|
{
|
|
try
|
|
{
|
|
var t = TradeHelper2.Deserialize(item.TradeJson);
|
|
t.id = item.TradeId;
|
|
t.BuySell = item.BuySell;
|
|
t.MetaDic["from_eod_trade"] = "true";
|
|
tradeList.Add(t);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
LogFactory.GetLogger("eod_trade.TradeJson解析").Error(ex);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------
|
|
// 没有出现在eodtrae表中的交易数据
|
|
//------------------------------------------------------
|
|
|
|
var eodTradeIds = tradeList.Select(n => n.id);
|
|
|
|
tradeDataPredicate = tradeDataPredicate.And(t => !eodTradeIds.Contains(t.id));
|
|
|
|
var trades = DbContext.trade.AsNoTracking().Where(tradeDataPredicate).ToArray();
|
|
|
|
//------------------------------------------------------
|
|
// 交易资金接口
|
|
//------------------------------------------------------
|
|
|
|
var tradeIds = trades.Select(x => x.id).ToList();
|
|
|
|
tradecashProvider = new TradeCashDataProvider().Initialize(eodTradeIds.Concat(tradeIds).ToArray());
|
|
var tradecashPreProvider = new TradeCashPreDataProvider().Initialize(eodTradeIds.Concat(tradeIds).ToArray());
|
|
|
|
//------------------------------------------------------
|
|
// 交易数据结果集
|
|
//------------------------------------------------------
|
|
|
|
if (!trades.Any())
|
|
{
|
|
return tradeList;
|
|
}
|
|
|
|
tradeList.AddRange(trades);
|
|
structProductTradeMetaList = null;
|
|
if (trades != null && trades.Length > 0)
|
|
{
|
|
var structProductTradeIds = trades.Where(p => "结构化产品".Equals(p.TradeType)).Select(p => p.id).Distinct().ToList();
|
|
if (structProductTradeIds != null && structProductTradeIds.Count > 0)
|
|
{
|
|
structProductTradeMetaList = DbContext.TradeMeta.AsNoTracking().Where(d => structProductTradeIds.Contains(d.TradeId)).ToList();
|
|
}
|
|
}
|
|
if (structProductTradeMetaList == null)
|
|
{
|
|
structProductTradeMetaList = new List<TradeMeta>();
|
|
}
|
|
foreach (var t in trades)
|
|
{
|
|
if (t.TradeType == "累计期权")
|
|
{
|
|
new OptionTradeActionRestoreService(this).RestoreTradeDataToSpecialDay(t, settleDate);
|
|
}
|
|
if (t.TradeType == "结构化产品")
|
|
{
|
|
var tradeMetas = structProductTradeMetaList.Where(d => d.TradeId == t.id).ToDictionary(g => g.MetaKey, g => g.MetaValue);
|
|
if (tradeMetas != null && tradeMetas.Count > 0)
|
|
{
|
|
foreach (var d in tradeMetas)
|
|
{
|
|
if (!t.MetaDic.ContainsKey(d.Key))
|
|
{
|
|
t.MetaDic.Add(d.Key, d.Value);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
ProcessTrade(settleDate, tradecashProvider, tradecashPreProvider, t);
|
|
}
|
|
|
|
return tradeList;
|
|
}
|
|
|
|
|
|
public static void ProcessTrade(DateTime settleDate, TradeCashDataProvider tradecashProvider, TradeCashPreDataProvider tradecashPreProvider, trade t)
|
|
{
|
|
//存在收盘之后的了结记录,代表收盘日那天,交易为确认成交状态
|
|
var tradeCashs = tradecashProvider.GetTradeCashes(t.id).Where(x => x.ValidState != "InValid" && !x.IsDeleted && x.Action != "系统操作-期权费" && (x.HappenedDate > settleDate || x.ValueDate > settleDate && x.HappenedDate == null));
|
|
t.TradeStatus = tradeCashs.Any() ? ConsTrade.确认成交 : t.TradeStatus;
|
|
|
|
//收历史盘时,找到收盘日之前最近一次的了结日期
|
|
var lastSecondTradeCash = tradecashProvider.GetTradeCashes(t.id).Where(x => x.ValidState != "InValid" && !x.IsDeleted && x.Action != "系统操作-期权费" && (x.HappenedDate <= settleDate || x.ValueDate <= settleDate && x.HappenedDate == null)).OrderByDescending(y => y.id).FirstOrDefault();
|
|
t.UnWindDate = lastSecondTradeCash?.HappenedDate ?? lastSecondTradeCash?.ValueDate;
|
|
|
|
if (ConsTrade.TradeCompleteStatus.Contains(t.TradeStatus))
|
|
{
|
|
t.Notional = 0;
|
|
t.TradeAmount = 0;
|
|
t.StockEqvNotional = 0;
|
|
}
|
|
else
|
|
{
|
|
var tc = tradeCashs.Where(x => x.Notional != 0).FirstOrDefault();
|
|
if (tc != null)
|
|
{
|
|
t.Notional = tc.Notional;
|
|
}
|
|
else
|
|
{
|
|
//取第一个ValueDate大于settleDate的TradeCash数据(TradeCashPreDataProvider类中ValueDate已做正序处理)
|
|
var tcPrePredicate = new Func<trade_cash_pre, bool>(x => x.Action != "系统操作-期权费" && x.Notional != 0
|
|
&& x.ValueDate > settleDate && (x.HappenedDate > settleDate || x.HappenedDate == null));
|
|
var tcPre = tradecashPreProvider.GetTradeCashPres(t.id).FirstOrDefault(tcPrePredicate);
|
|
if (tcPre != null)
|
|
{
|
|
t.Notional = tcPre.Notional;
|
|
}
|
|
}
|
|
|
|
if (t.TradeType == ConsGlobal.TradeType.CashFlow)
|
|
{
|
|
t.TradeAmount = 0;
|
|
t.StockEqvNotional = t.Notional;
|
|
}
|
|
else
|
|
{
|
|
var CountRatio = (DataCacheProvider.GetUnderlyingDataSource().GetData(t.UnderlyingId)?.CountRatio) ?? 1;
|
|
t.TradeAmount = t.Notional / CountRatio;
|
|
t.StockEqvNotional = t.OriginalNotional == 0 ? 0 : (t.Notional / t.OriginalNotional * t.OriginalStockEqvNotional) ?? 0;
|
|
}
|
|
}
|
|
|
|
tradeBLL.SetFieldsByTradeType(t, settleDate);
|
|
}
|
|
}
|
|
}
|