从山证v2.3.0拷贝

This commit is contained in:
吴方海
2024-05-09 14:06:26 +08:00
parent 566ff33259
commit f9d8a256a6
4471 changed files with 1203456 additions and 9 deletions
@@ -0,0 +1,138 @@
using System.Linq.Expressions;
using YLErp.Model.Enum;
namespace YLErp.Modules.EodModule.SettlementModule
{
public class EodQueryPredicateBuilder
{
readonly DateTime _startDate;
readonly EodSettlementRequest _request;
Expression<Func<trade, bool>> _otcTradePredicate;
Expression<Func<ExchangeTrade, bool>> _exchangeTradePredicate;
Expression<Func<clientcashincashout_product, bool>> _clientProductPredicate;
public EodQueryPredicateBuilder(EodSettlementRequest request)
{
_request = request ?? throw new ArgumentNullException(nameof(request));
if (_request.SettleDate.Year < 2000)
{
throw new ArgumentException("无效request");
}
_startDate = request.SettleDate.AddYears(-5);
}
/// <summary>
/// 构建结算用的衍生品交易筛选条件
/// </summary>
/// <param name="isGuoJunNeedBesideSwapTrade">国君是否需要互换交易--国君互换持仓计算暂时自己导入 true 需要</param>
/// <returns></returns>
public Expression<Func<trade, bool>> GetOtcTradePredicate(bool isGuoJunNeedBesideSwapTrade = false)
{
if (_otcTradePredicate != null && !PS.Config.IsGuoJun)
{
return _otcTradePredicate;
}
if (!_request.IsSettleOtcTrades)
{
return _otcTradePredicate = PredicateBuilder.False<trade>();
}
//如果前一天是假日,要显示包含假日的交易
var preday = BLL.valuedateBLL.GetNonHolidayDefore(_request.SettleDate.AddDays(-1));
var predicate = PredicateBuilder.Create<trade>(t =>
t.TradeDate > _startDate && t.TradeDate <= _request.SettleDate
&& t.ValidState != ConsGlobal.InValid && t.ClientId > 0
&& t.TradeType != "结构化交易" && t.TradeType != "收益互换"
&& (t.ExerciseDate > preday || t.UnWindDate > preday || _request.SuspensionUnderlyingIds.Contains(t.UnderlyingId) || _request.DelayUnderlyIds.Contains(t.UnderlyingId) || t.SettlementFlag == 1)
&& (!ConsTrade.TradeCompleteStatus.Contains(t.TradeStatus) || t.UnWindDate > preday));
if (PS.Config.IsGuoJun && !isGuoJunNeedBesideSwapTrade)
{
predicate = PredicateBuilder.Create<trade>(t =>
t.TradeDate > _startDate && t.TradeDate <= _request.SettleDate
&& t.ValidState != ConsGlobal.InValid && t.ClientId > 0
&& t.TradeType != "结构化交易" && t.TradeType != "收益互换"
&& (t.ExerciseDate > preday || t.UnWindDate > preday || _request.SuspensionUnderlyingIds.Contains(t.UnderlyingId) || _request.DelayUnderlyIds.Contains(t.UnderlyingId) || t.SettlementFlag == 1)
&& (!ConsTrade.TradeCompleteStatus.Contains(t.TradeStatus) || t.UnWindDate > preday));
}
if (_request.ClientIds != null && _request.ClientIds.Any())
{
predicate = predicate.And(t => _request.ClientIds.Contains(t.ClientId));
}
return _otcTradePredicate = predicate;
}
public Expression<Func<trade, bool>> GetOtcSwapTradePredicate()
{
//如果前一天是假日,要显示包含假日的交易
var preday = BLL.valuedateBLL.GetNonHolidayDefore(_request.SettleDate.AddDays(-1));
var predicate = PredicateBuilder.Create<trade>(t =>
t.TradeDate > _startDate && t.TradeDate <= _request.SettleDate
&& t.ValidState != ConsGlobal.InValid
&& t.TradeType == "收益互换"
&& (t.ExerciseDate > preday || t.UnWindDate > preday || t.SettlementFlag == 1)
&& (!ConsTrade.TradeCompleteStatus.Contains(t.TradeStatus) || t.UnWindDate > preday));
if (_request.ClientIds != null && _request.ClientIds.Any())
{
predicate = predicate.And(t => _request.ClientIds.Contains(t.ClientId));
}
return _otcTradePredicate = predicate;
}
/// <summary>
/// 构建结算用的场内交易筛选条件
/// </summary>
public Expression<Func<ExchangeTrade, bool>> GetExchangeTradePredicate()
{
if (_exchangeTradePredicate != null)
{
return _exchangeTradePredicate;
}
if (!_request.IsSettleExchangeTrades)
{
return _exchangeTradePredicate = PredicateBuilder.False<ExchangeTrade>();
}
var predicate = PredicateBuilder.Create<ExchangeTrade>(t => t.TradeDate == _request.SettleDate && t.IsValid);
return _exchangeTradePredicate = predicate;
}
/// <summary>
/// 客户抵押品查询
/// </summary>
public Expression<Func<clientcashincashout_product, bool>> GetClientProductPredicate()
{
if (_clientProductPredicate != null)
{
return _clientProductPredicate;
}
if (!_request.IsSettleOtcTrades)
{
return _clientProductPredicate = PredicateBuilder.False<clientcashincashout_product>();
}
var settleDateEnd = _request.SettleDate.AddDays(1);
var predicate = PredicateBuilder.Create<clientcashincashout_product>(t => t.HappenDate < settleDateEnd
&& (t.Status == Clientcashincashout_productStatusEnum..ToString() && t.OptStatus == ClientCashInCashOut.
|| t.Status == Clientcashincashout_productStatusEnum..ToString() && (t.OptStatus != ClientCashInCashOut. || t.BackDate >= settleDateEnd)));
if (_request.ClientIds != null && _request.ClientIds.Any())
{
predicate = predicate.And(t => t.ClientId != 0 && _request.ClientIds.Contains(t.ClientId));
}
return predicate;
}
}
}
@@ -0,0 +1,338 @@
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.ToArray();
List<TradeMeta> structProductTradeMetaList = null;
if (etList != null && etList.Length > 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);
}
}
}
@@ -0,0 +1,20 @@
namespace YLErp.Modules.EodModule.SettlementModule
{
/// <summary>
/// 结算服务基类
/// </summary>
public abstract class EodSettleServiceBase : YLBaseService
{
protected readonly EodSettlementContextBase _context;
public EodSettleServiceBase(EodSettlementContextBase context)
: base(context.UserInfo)
{
_context = context ?? throw new ArgumentNullException(nameof(context));
ResetDbContext();
}
public DateTime SettleDate => _context.SettleDate;
}
}
@@ -0,0 +1,236 @@
using YLErp.Abstract.DataProviders;
using YLErp.Modules.DataProviderModule;
namespace YLErp.Modules.EodModule.SettlementModule
{
/// <summary>
/// 这是一个多线程运行环境下的收盘上下文类
/// </summary>
public abstract class EodSettlementContextBase
{
EodPriceProvider _eodPriceProvider;
EodPriceProvider _preEodPriceProvider;
ExchangeStatus _exchangeStatus;
protected EodSettlementContextBase(EodSettlementRequest request, CancellationToken cancellationToken)
{
Request = request ?? throw new ArgumentNullException(nameof(request));
CancellationToken = cancellationToken;
if (request.SettleDate.Year < 2000)
{
throw new ArgumentException("request参数错误:SettleDate");
}
if (!request.IsSettleOtcTrades && !request.IsSettleExchangeTrades)
{
throw new ArgumentException("request参数错误");
}
StartDate = request.SettleDate.AddYears(-3);
PreSettleDate = BLL.valuedateBLL.GetNonHolidayDefore(SettleDate.AddDays(-1));
SystemValue = BLL.valuedateBLL.SystemDate;
PredicateBuilder = new EodQueryPredicateBuilder(request);
}
public ExchangeStatus ExchangeStatus => _exchangeStatus ?? (_exchangeStatus = new ExchangeStatus(SettleDate, PreSettleDate));
/// <summary>
/// 任务终止令牌
/// </summary>
public CancellationToken CancellationToken { get; }
/// <summary>
/// 结算请求参数
/// </summary>
public EodSettlementRequest Request { get; }
/// <summary>
/// 请求用户信息
/// </summary>
public OptUserInfo UserInfo => Request.UserInfo;
/// <summary>
/// 结算日
/// </summary>
public DateTime SettleDate => Request.SettleDate;
/// <summary>
/// 需要收盘得客户
/// </summary>
public IEnumerable<int> ClienIds => Request.ClientIds;
/// <summary>
/// 结算日
/// </summary>
public string SettleDateStr => Request.SettleDate.ToString(ConsGlobal.DateFormat);
/// <summary>
/// 获取上个结算日(结算第一步就需要检查这个日期是否等于日历数据中的前一工作日)
/// </summary>
public DateTime PreSettleDate { get; }
/// <summary>
/// 获取上个结算日(结算第一步就需要检查这个日期是否等于日历数据中的前一工作日)
/// </summary>
public string PreSettleDateStr => PreSettleDate.ToString(ConsGlobal.DateFormat);
/// <summary>
/// 交易起始关注日
/// </summary>
public DateTime StartDate { get; }
/// <summary>
/// 系统参数
/// </summary>
public valuedate SystemValue { get; }
/// <summary>
/// 查询条件构造器
/// </summary>
public EodQueryPredicateBuilder PredicateBuilder { get; }
/// <summary>
/// 是否当前交易日
/// </summary>
public bool IsCurrentDay => SettleDate == SystemValue.ValueDate;
/// <summary>
/// 获取日终价格提供对象
/// </summary>
public EodPriceProvider GetEodPriceProvider()
{
if (_eodPriceProvider == null)
{
_eodPriceProvider = new EodPriceProvider(SettleDate, isDiviendPrice: false);
if (!Request.IsPartialSettlement)
{
_eodPriceProvider.Initialize();
}
}
return _eodPriceProvider;
}
/// <summary>
/// 收盘日上一交易日日终价格提供
/// </summary>
public EodPriceProvider GetPreEodPriceProvider()
{
if (_preEodPriceProvider == null)
{
_preEodPriceProvider = new EodPriceProvider(PreSettleDate);
_preEodPriceProvider.SetPreValueDate(PreSettleDate);
if (!Request.IsPartialSettlement)
{
_preEodPriceProvider.Initialize();
}
}
return _preEodPriceProvider;
}
#region--------
/// <summary>
/// 记录日志
/// </summary>
public void LogInfo(string message)
{
LogFactory.GetLogger("日终结算").Info(message);
}
/// <summary>
/// 记录日志
/// </summary>
public void LogError(string message, Exception ex = null)
{
LogFactory.GetLogger("日终结算").Error(message, ex);
}
/// <summary>
/// 激发错误异常
/// </summary>
public void RaiseError(string action, string error, Exception ex = null)
{
if (error == null)
{
error = "发生错误";
}
throw new EodSettleException($"[{action}]{error}", ex);
}
/// <summary>
/// 检查结算任务是否被取消
/// </summary>
public void CheckCanceled()
{
CancellationToken.ThrowIfCancellationRequested();
}
#endregion
public override string ToString()
{
return SettleDateStr;
}
}
/// <summary>
/// 日终结算错误
/// </summary>
public class EodSettleException : Exception
{
public EodSettleException(string message, Exception innerException)
: base(message, innerException)
{
}
}
/// <summary>
/// 交易所状态
/// </summary>
public class ExchangeStatus
{
readonly DateTime _preDate;
readonly Dictionary<string, (bool isholidy, DateTime exPreDate)> _dic;
public ExchangeStatus(DateTime valueDate, DateTime preDate)
{
_preDate = preDate;
_dic = new Dictionary<string, (bool, DateTime)>(StringComparer.OrdinalIgnoreCase);
foreach (var m in DataCacheProvider.GetMarketDataSource().AsQueryable())
{
if (!string.IsNullOrEmpty(m.ExchangeNo))
{
var calendar = m.CalendarName.TrimToNull() ?? ConsGlobal.DefaultCalendar;
var isholidy = QdpModule.QdpCalendarHelper.IsHoliday(valueDate, calendar);
var exPreDate = QdpModule.QdpCalendarHelper.GetNonHolidayDefore(_preDate, calendar);
_dic[m.ExchangeNo] = (isholidy, exPreDate);
}
}
}
/// <summary>
/// 交易所是否节假日
/// </summary>
public bool IsHoliday(string exchangeNo)
{
return !string.IsNullOrEmpty(exchangeNo) && _dic.TryGetValue(exchangeNo, out var state) && state.isholidy;
}
/// <summary>
/// 交易所的上一个交易日
/// </summary>
public DateTime GetPreDate(string exchangeNo)
{
return !string.IsNullOrEmpty(exchangeNo) && _dic.TryGetValue(exchangeNo, out var state) ? state.exPreDate : _preDate;
}
}
}
@@ -0,0 +1,181 @@
namespace YLErp.Modules.EodModule.SettlementModule
{
/// <summary>
/// 日终结算请求
/// </summary>
public class EodSettlementRequest
{
bool _readonly;
private DateTime _settleDate;
protected string _volType;
protected bool _useClosePrice;
private IEnumerable<int> _clientId;
private bool _isSettleOtcTrades = true;
private bool _isSettleExchangeTrades = true;
public EodSettlementRequest(OptUserInfo userInfo)
{
UserInfo = userInfo ?? throw new ArgumentNullException(nameof(userInfo));
SuspensionUnderlyingIds = EodDataHelper.GetSuspensionUnderlyingIds();
}
#region--------
public bool Readonly => _readonly;
private void CheckReadonly()
{
if (_readonly)
{
throw new InvalidOperationException();
}
}
public EodSettlementRequest AsReadonly()
{
_readonly = true;
return this;
}
#endregion
public OptUserInfo UserInfo { get; }
/// <summary>
/// 结算日期
/// </summary>
public DateTime SettleDate
{
get => _settleDate;
set
{
CheckReadonly();
_settleDate = value;
}
}
/// <summary>
/// 结算用的波动率类型
/// </summary>
public string VolType
{
get => _volType;
set
{
CheckReadonly();
_volType = value;
}
}
/// <summary>
/// 是否使用收盘价结算
/// </summary>
public bool UseClosePrice
{
get => _useClosePrice;
set
{
CheckReadonly();
_useClosePrice = value;
}
}
/// <summary>
/// 是否结算衍生品交易(默认true)
/// </summary>
public bool IsSettleOtcTrades
{
get => _isSettleOtcTrades;
set
{
CheckReadonly();
_isSettleOtcTrades = value;
}
}
/// <summary>
/// 只结算所给客户ID的交易
/// 注意:只用于衍生品交易结算时
/// </summary>
public IEnumerable<int> ClientIds
{
get => _clientId;
set
{
CheckReadonly();
_clientId = value;
}
}
/// <summary>
/// 是否结算场内交易(默认true)
/// </summary>
public bool IsSettleExchangeTrades
{
get => _isSettleExchangeTrades;
set
{
CheckReadonly();
_isSettleExchangeTrades = value;
}
}
/// <summary>
/// 停牌标的ID集合
/// </summary>
public IEnumerable<int> SuspensionUnderlyingIds { get; }
/// <summary>
/// 涨跌停标的集合
/// </summary>
public IEnumerable<int> DelayUnderlyIds { get; set; }
/// <summary>
/// 是否部分结算
/// </summary>
public bool IsPartialSettlement
{
get
{
if (ClientIds!=null)
{
return true;
}
return !(IsSettleOtcTrades && IsSettleExchangeTrades);
}
}
/// <summary>
/// 是否延期交易
/// </summary>
public bool IsDelayTrade { get; set; }
/// <summary>
/// 克隆对象(克隆后非只读)
/// </summary>
public EodSettlementRequest Clone(bool asReadonly = false)
{
var clone = (EodSettlementRequest)MemberwiseClone();
clone._readonly = asReadonly;
return clone;
}
/// <summary>
/// 返回收盘价或结算价
/// </summary>
public string GetPriceType()
{
return UseClosePrice ? ConsGlobal.SettlePriceMode. : ConsGlobal.SettlePriceMode.;
}
public override string ToString()
{
var priceType = UseClosePrice ? "收盘价" : "结算价";
return $"{SettleDate:yyyy-MM-dd},{VolType},{priceType}";
}
}
}
@@ -0,0 +1,304 @@
using YLErp.DBModels.Consts;
using YLErp.Modules.VolatilityModule;
using YLErp.QdpModule;
namespace YLErp.Modules.EodModule.SettlementModule
{
/// <summary>
/// 结算波动率接口(暂未考虑多标的处理)
/// </summary>
public interface IEodVolProvider
{
double? GetVol(OtcTradeBase td, double spotPrice);
}
#region--------
/// <summary>
/// TradeVol模式持仓波动率提供
/// </summary>
class EodPositionVolProvider_Trade : TradeVolitalityProvider, IEodVolProvider
{
Dictionary<int, double> _overridVolDic;
public EodPositionVolProvider_Trade(DateTime valueDate) : base(valueDate)
{
}
public double? GetVol(OtcTradeBase td, double spotPrice)
{
if (td is null)
{
throw new ArgumentNullException(nameof(td));
}
if (td.id < 1)
{
return null;
}
if (_overridVolDic == null)
{
using (var db = DbContextFactory.GetYLDbContext())
{
_overridVolDic = db.eod_trade_vol_override.Where(x => x.valuedate == _valueDate)
.Select(n => new { n.tradeid, n.vol }).ToDictionary(n => n.tradeid, m => m.vol);
}
}
if (_overridVolDic.TryGetValue(td.id, out var vol))
{
return vol;
}
if (TryGetVol(td.id, td.ExerciseDate.Value, out vol))
{
return vol;
}
return VolatilityHelper.GetTradeVol(_valueDate, td.TradeDate.Value, td.ExerciseDate.Value,
td.TradeOpenVolatility ?? 0, td.TradeCloseVolatility ?? 0, td.NumOfSmoothingDays ?? 0);
}
}
#endregion
#region--------
/// <summary>
/// 曲面波动率模式持仓波动率提供
/// </summary>
class EodUnderlyingVolProvider : UnderlyingVolitalityProvider
{
protected EodUnderlyingVolProvider(DateTime valueDate, IEnumerable<string> volTypes) : base(valueDate, volTypes)
{
}
protected double? InnerGetVol(OtcTradeBase td, string volType, double spotPrice)
{
if (td is null)
{
throw new ArgumentNullException(nameof(td));
}
var userGroup = string.Empty;
if (ConsUserGroup.HasGroup)
{
userGroup = DataCacheProvider.GetAssetUnitDataSource().GetData(td.AssetId)?.UserGroup;
if (string.IsNullOrEmpty(userGroup))
{
throw new Exception($"[结算交易'{td.TradeNumber}'][取{volType}波动率]未能获取用户组!");
}
}
var unVol = GetVol(volType, userGroup, td.UnderlyingCode);
if (unVol == null)
{
return null;
}
var constVol = VolatilityHelper.GetInterpolatedVol(
volConstructionType: PS.Config.ErpElement.SkewMapVolConstruction ? VolConstructionType.SkewMap : VolConstructionType.Normal,
volSurface: unVol,
valueDate: _valueDate,
underlyingCode: td.UnderlyingCode,
exerciseDate: td.ExerciseDate.Value,
strike: td.Strike ?? 0,
isBuy: td.BuySell == "买入",
isCall: td.CallPut == "Call",
spotPrice: spotPrice,
isMoneynessOption: td.IsMoneynessOption == "是",
isEodCalc: true);
return constVol;
}
}
/// <summary>
/// 单个曲面波动率类型
/// </summary>
class EodUnderlyingSingleVolProvider : EodUnderlyingVolProvider, IEodVolProvider
{
readonly string _volType;
public EodUnderlyingSingleVolProvider(DateTime valueDate, string volType)
: base(valueDate, new[] { volType })
{
_volType = volType ?? throw new ArgumentNullException(nameof(volType));
}
public double? GetVol(OtcTradeBase td, double spotPrice)
{
return InnerGetVol(td, _volType, spotPrice);
}
}
/// <summary>
/// 曲面波动率模式持仓波动率提供
/// </summary>
class EodPositionVolProvider_Underlying : EodUnderlyingVolProvider, IEodVolProvider
{
readonly string _volType;
Dictionary<int, double> _overridVolDic;
public EodPositionVolProvider_Underlying(DateTime valueDate, string volType)
: base(valueDate, new[] { volType })
{
_volType = volType ?? throw new ArgumentNullException(nameof(volType));
}
public double? GetVol(OtcTradeBase td, double spotPrice)
{
if (_overridVolDic == null)
{
using (var db = DbContextFactory.GetYLDbContext())
{
_overridVolDic = db.eod_trade_vol_override.Where(x => x.valuedate == _valueDate)
.Select(n => new { n.tradeid, n.vol }).ToDictionary(n => n.tradeid, m => m.vol);
}
}
if (_overridVolDic.TryGetValue(td.id, out var vol))
{
return vol;
}
return InnerGetVol(td, _volType, spotPrice);
}
}
#endregion
#region--------
/// <summary>
/// 对冲波动率提供
/// </summary>
class EodHedgeVolProvider : TradeHedgeVolProvider, IEodVolProvider
{
public EodHedgeVolProvider(DateTime valueDate) : base(valueDate)
{
}
public double? GetVol(OtcTradeBase td, double spotPrice)
{
if (td is null)
{
throw new ArgumentNullException(nameof(td));
}
if (TryGetVol(td.id, out var vol))
{
return vol;
}
return td.TradeSavedVol ?? 0;
}
}
/// <summary>
/// 开仓波动率提供(开仓波动率只在TradeVol模式下结算)
/// </summary>
class EodOpenVolProvider : TradeHedgeVolProvider, IEodVolProvider
{
public EodOpenVolProvider(DateTime valueDate) : base(valueDate)
{
}
public double? GetVol(OtcTradeBase td, double spotPrice)
{
return td?.TradeOpenVolatility;
}
}
#endregion
#region--------
/// <summary>
/// 扩展波动率(光证)提供
/// </summary>
class EodExtendVolProvider_GZ : VarietyVolProvider, IEodVolProvider
{
public EodExtendVolProvider_GZ(DateTime valueDate) : base(valueDate)
{
}
public double? GetVol(OtcTradeBase td, double spotPrice)
{
if (td is null)
{
throw new ArgumentNullException(nameof(td));
}
if (TryGetVol(td.UnderlyingCode, out var vol))
{
return vol;
}
return null;
}
}
/// <summary>
/// 扩展波动率(BidAskVol)提供
/// </summary>
class EodExtendVolProvider_BidAskVol : EodUnderlyingVolProvider, IEodVolProvider
{
public EodExtendVolProvider_BidAskVol(DateTime valueDate)
: base(valueDate, new[] { "报价Ask", "报价Bid" })
{
}
public double? GetVol(OtcTradeBase td, double spotPrice)
{
if (td is null)
{
throw new ArgumentNullException(nameof(td));
}
return InnerGetVol(td, td.BuySell == "买入" ? "报价Ask" : "报价Bid", spotPrice);
}
}
#endregion
/// <summary>
/// 波动率提供者工厂
/// </summary>
public class EodVolProviderFactory
{
public static IEodVolProvider GetEodVolProvider(DateTime valueDate, string volType)
{
switch (volType)
{
case "持仓":
if (PS.Config.IsTradeVol)
{
return new EodPositionVolProvider_Trade(valueDate);
}
volType = BLL.valuedateBLL.SystemDate.EodSettleVolMode.TrimToNull() ?? "财务";
return new EodPositionVolProvider_Underlying(valueDate, volType);
case "开仓":
return new EodOpenVolProvider(valueDate);
case "对冲":
return new EodHedgeVolProvider(valueDate);
case "BidAskVol":
return new EodExtendVolProvider_BidAskVol(valueDate);
case "光证":
return new EodExtendVolProvider_GZ(valueDate);
default:
return new EodUnderlyingSingleVolProvider(valueDate, volType);
}
}
}
}
@@ -0,0 +1,9 @@
namespace YLErp.Modules.EodModule.SettlementModule
{
public interface IAsyncTaskManager
{
void SetTaskStep(string step);
void WaitTasks(params Task[] tasks);
}
}