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

643 lines
25 KiB
C#

using System.Text;
using YLErp.DBModels.Consts;
using YLErp.Models;
using YLErp.Modules.TradeModule.DealModule;
using YLErp.QdpModule;
namespace YLErp.Modules.TradeModule.AccumulatorOptionModule
{
public class TradeAccumulatorChangeService : TradeActionDataService
{
public TradeAccumulatorChangeService(OptUserInfo userInfo) : base(userInfo)
{
}
private static Func<TradeAction, bool> BasePredicate(int tradeId) =>
n => n.TradeId == tradeId && n.IsValid && n.ActionType == TradeActionType.AccumulatorChange;
/// <summary>
/// 获取历史操作数据
/// </summary>
public IEnumerable<TradeAccumulatorChangeData> GetHisDatas(int tradeId)
{
var hisPredicate = BasePredicate(tradeId);
var hisDatas = new TradeActionQueryService(this).GetDatas(hisPredicate);
return hisDatas.Select(n =>
{
var data = JsonHelper.Deserialize<TradeAccumulatorChangeData>(n.ActionData);
data.ValueDate = n.ValueDate.OtcFormatDate();
return data;
}).ToArray();
}
#region----保存/删除换月数据----
/// <summary>
/// 保存换月数据
/// </summary>
/// <param name="tradeId"></param>
/// <param name="changeData"></param>
/// <param name="removeObCheckDatas">是否删除累计收益记录</param>
public TradeAccumulatorChangeResult SaveChangeData(int tradeId, TradeAccumulatorChangeData changeData, bool removeObCheckDatas)
{
if (!DateTime.TryParse(changeData.ValueDate, out var valueDate))
{
throw new ServiceException("解析换月日期失败:" + changeData.ValueDate);
}
var td = DbContext.trade.Find(tradeId);
if (td == null)
{
throw new ServiceException("找不到交易信息,tradeId:" + tradeId);
}
if (td.TradeStatus != "确认成交")
{
throw new ServiceException("当前交易状态不符合设置换月的条件");
}
if (valueDate <= td.TradeDate.Value)
{
throw new ServiceException($"换月日期'需大于交易日期'{td.TradeDate:yyyy-MM-dd}'");
}
if (valueDate >= td.ExerciseDate.Value)
{
throw new ServiceException($"换月日期需小于到期日期'{td.ExerciseDate:yyyy-MM-dd}'");
}
if (valueDate > BLL.valuedateBLL.ValueDate)
{
throw new ServiceException("换月日期不能大于系统日期:" + changeData.ValueDate);
}
var tdAcc = DbContext.trade_accumulator_option.Where(n => n.TradeId == tradeId).FirstOrDefault();
if (tdAcc == null)
{
throw new ServiceException("找不到交易扩展信息,交易编号:" + td.TradeNumber);
}
var um = DataCacheProvider.GetUnderlyingDataSource().GetData(changeData.UnderlyingCode);
if (um == null)
{
throw new ServiceException("找不到标的信息:" + changeData.UnderlyingCode);
}
if (um.IsFutures() && um.MaturityDate.HasValue && um.MaturityDate < valueDate)
{
throw new ServiceException($"所选标的'{changeData.UnderlyingCode}'的到期日'{um.MaturityDate:yyyy-MM-dd}'应大于换月日期'{valueDate:yyyy-MM-dd}'");
}
//按换月日期正序排序获取换月设置集合
var basePredicate = BasePredicate(td.id);
var dbActions = DbContext.TradeAction.Where(basePredicate).OrderBy(n => n.ValueDate).ToArray();
//将已有Action数据无效
foreach (var exist in dbActions.Where(x => x.ValueDate == valueDate))
{
exist.IsValid = false;
}
//更新交易数据(如果当前存入的换月数据是最新一条数据)
var tradeUpdated = false;
TradeAction firstDayAction = null;
var nextAction = dbActions.FirstOrDefault(a => a.ValueDate > valueDate);
if (nextAction == null)//没有大于当前换月日期的数据则需要更新交易要素
{
if (valueDate != td.TradeDate.Value && !dbActions.Any(a => a.ValueDate == td.TradeDate.Value))
{
firstDayAction = GetFirstDayChangeData(td, tdAcc);
}
tradeUpdated = true;
RestoreAccumulatorChangeData(td, tdAcc, changeData, um);
}
else
{
tradeUpdated = true;
UpdateTradeObDates(tdAcc, changeData, nextAction.ValueDate);
}
//新增Action数据
var curAction = CreateTradeAction(tradeId, valueDate, TradeActionType.AccumulatorChange);
curAction.ActionRemark = "换月";
curAction.ActionData = JsonHelper.Serialize(changeData);
var addActions = firstDayAction != null ? new[] { firstDayAction, curAction } : new[] { curAction };
//移除累计收益记录
if (removeObCheckDatas)
{
var nextDate = dbActions.Where(n => n.ValueDate > valueDate).FirstOrDefault()?.ValueDate;
if (!nextDate.HasValue)
{
nextDate = td.ExerciseDate.Value.AddYears(1);
}
var obDatas = DbContext.autocall_observation.Where(n => n.TradeId == td.id && n.EndDate >= valueDate && n.EndDate < nextDate).ToArray();
if (obDatas.Any())
{
tradeUpdated = true;
DbContext.autocall_observation.RemoveRange(obDatas);
//var cashIds = obDatas.Where(n => n.CashId > 0).Select(n => n.CashId ?? 0).ToArray();
//RemoveEodTradeAndFutureInfo(saveChanges: false, tradeId: td.id, removeStartDate: valueDate, tradeCashIds: cashIds, deleteOtherCashs: false);
}
//移除自定义观察数据
var manualPriceDatas = DbContext.manually_trade_observation_price.Where(n => n.TradeId == td.id && n.ValueDate >= valueDate).ToArray();
DbContext.manually_trade_observation_price.RemoveRange(manualPriceDatas);
new TradeBackService(this).Execute(tradeId, valueDate);
}
DbContext.TradeAction.AddRange(addActions);
AddTradeOperationHistoryAndSetParentTradeInfo(saveChanges: false, trade: td, optType: "设置累计换月", comments: $"{changeData.ValueDate},{changeData.UnderlyingCode}");
#region 更新metadic的观察频率数据
var tMeta = DbContext.TradeMeta.FirstOrDefault(n => n.TradeId == tradeId && n.MetaKey == ConsTradeMetaKey.koObservationRate);
if (tMeta != null)
{
tMeta.MetaValue = changeData.KOObservation;
}
else
{
tMeta = new TradeMeta
{
TradeId = tradeId,
MetaKey = ConsTradeMetaKey.koObservationRate,
MetaValue = changeData.KOObservation,
CreateTime = DateTime.Now
};
DbContext.TradeMeta.Add(tMeta);
}
#endregion
DbContext.SaveChanges();
return new TradeAccumulatorChangeResult
{
Actions = addActions,
TradeUpdated = tradeUpdated
};
}
//还原或更新更新交易要素
private void RestoreAccumulatorChangeData(OtcTradeBase td, trade_accumulator_option tdAcc, TradeAccumulatorChangeData changeData, underlying_manager um = null)
{
var clone = changeData.Clone();
var isFixed = tdAcc.PayoffType == "固定";
var preList = ParseKoObservation(tdAcc.KOObservationDates, isFixed ? null : tdAcc.KOObservationSettleDates, td.TradeDate.Value, DateTime.Parse(changeData.ValueDate));
var curList = ParseKoObservation(changeData.KOObservationDates, isFixed ? null : changeData.KOObservationSettleDates, DateTime.MinValue);
preList.AddRange(curList);
(string KOObservationDates, string KOObservationSettleDates) = BuildKOObservationDates(preList, true);
clone.KOObservationDates = KOObservationDates;
clone.KOObservationSettleDates = KOObservationSettleDates;
TradeAccumulatorHelper.RestoreAccumulatorChangeData(td, tdAcc, clone, um);
}
//更新交易要素--观察频率
private void UpdateTradeObDates(trade_accumulator_option tdAcc, TradeAccumulatorChangeData curChangeData, DateTime nextValueDate)
{
var isFixed = tdAcc.PayoffType == "固定";
var curValueDate = DateTime.Parse(curChangeData.ValueDate);
var preList = ParseKoObservation(tdAcc.KOObservationDates, isFixed ? null : tdAcc.KOObservationSettleDates, DateTime.MinValue, curValueDate);
var curList = ParseKoObservation(curChangeData.KOObservationDates, isFixed ? null : curChangeData.KOObservationSettleDates, DateTime.MinValue, nextValueDate);
var nextList = ParseKoObservation(tdAcc.KOObservationDates, isFixed ? null : tdAcc.KOObservationSettleDates, nextValueDate);
preList.AddRange(curList);
preList.AddRange(nextList);
(string KOObservationDates, string KOObservationSettleDates) = BuildKOObservationDates(preList, true);
tdAcc.KOObservationDates = KOObservationDates;
tdAcc.KOObservationSettleDates = KOObservationSettleDates;
}
//获取交易开始日期的换月存储数据
private TradeAction GetFirstDayChangeData(OtcTradeBase td, trade_accumulator_option tdAcc)
{
var action = CreateTradeAction(td.id, td.TradeDate.Value, TradeActionType.AccumulatorChange);
action.ActionRemark = "换月";
var data = new TradeAccumulatorChangeData
{
KOBarrier = tdAcc.KOBarrier,
KOObservationDates = tdAcc.KOObservationDates,
KOObservationSettleDates = tdAcc.KOObservationSettleDates,
Strike = td.Strike ?? 0,
UnderlyingCode = td.UnderlyingCode,
ValueDate = td.TradeDate.Value.ToString("yyyy-MM-dd")
};
action.ActionData = JsonHelper.Serialize(data);
return action;
}
/// <summary>
/// 删除换月操作
/// </summary>
public TradeAccumulatorChangeResult RemoveChangeData(DateTime valueDate, int tradeId)
{
var td = DbContext.trade.Find(tradeId);
if (td == null)
{
throw new ServiceException("交易信息不存在");
}
if (valueDate == td.TradeDate.Value)
{
throw new ServiceException("初始记录不允许删除");
}
var predicate = BasePredicate(tradeId);
var dbActions = DbContext.TradeAction.Where(predicate).OrderBy(n => n.ValueDate).ToList();
var index = dbActions.FindIndex(n => n.ValueDate == valueDate);
if (index < 0)
{
throw new ServiceException("操作记录已经不存在");
}
if (index == 0)
{
//不应该出现的情况,如果出现了还是报错吧
throw new ServiceException("初始记录丢失,操作不能继续");
}
var removeAction = dbActions[index];
var tradeUpdated = false;
var invalidActions = new List<TradeAction> { removeAction };
var tdAcc = DbContext.trade_accumulator_option.Where(n => n.TradeId == tradeId).FirstOrDefault();
if (tdAcc == null)
{
throw new ServiceException("找不到交易扩展信息,交易编号:" + td.TradeNumber);
}
//交易数据还原到上条操作状态
if (index == dbActions.Count - 1)
{
if (index == 0)
{
throw new ServiceException("未找到初始记录,操作不能继续");
}
var preAction = dbActions[index - 1];
if (string.IsNullOrWhiteSpace(preAction.ActionData))
{
throw new ServiceException("上条操作记录备份数据已经被损坏,交易数据还原失败,操作不能继续");
}
var changeData = JsonHelper.Deserialize<TradeAccumulatorChangeData>(preAction.ActionData);
var um = DataCacheProvider.GetUnderlyingDataSource().GetData(changeData.UnderlyingCode);
if (um == null)
{
throw new ServiceException("交易数据还原失败,找不到标的信息:" + changeData.UnderlyingCode);
}
RestoreAccumulatorChangeData(td, tdAcc, changeData, um);
if (preAction.ValueDate == td.TradeDate.Value)
{
preAction.IsValid = false;
invalidActions.Insert(0, preAction);
}
tradeUpdated = true;
}
else
{
var preAction = dbActions[index - 1];
var preData = JsonHelper.Deserialize<TradeAccumulatorChangeData>(preAction.ActionData);
UpdateTradeObDates(tdAcc, preData, dbActions[index + 1].ValueDate);
tradeUpdated = true;
}
removeAction.IsValid = false;
var curChangeData = JsonHelper.Deserialize<TradeAccumulatorChangeData>(removeAction.ActionData);
AddTradeOperationHistoryAndSetParentTradeInfo(saveChanges: false, trade: td, optType: "移除累计换月", comments: $"{valueDate:yyyy-MM-dd},{curChangeData.UnderlyingCode}");
DbContext.SaveChanges();
return new TradeAccumulatorChangeResult
{
Actions = invalidActions,
TradeUpdated = tradeUpdated
};
}
#endregion
/// <summary>
/// 根据提供的数据获取换期数据
/// </summary>
public TradeAccumulatorChangeDataEx GetChangeData(DateTime valueDate, OtcTradeBase td, trade_accumulator_option tdAcc = null)
{
if (td is null)
{
throw new ArgumentNullException(nameof(td));
}
if (valueDate > td.ExerciseDate.Value)
{
valueDate = td.ExerciseDate.Value;
}
var predicate = BasePredicate(td.id);
var query = from a in DbContext.TradeAction.Where(predicate)
orderby a.ValueDate descending
select new
{
a.ValueDate,
a.ActionData
};
var dbActions = query.ToArray();
TradeAccumulatorChangeDataEx changeData;
var dbAction = query.Where(x => x.ValueDate <= valueDate).FirstOrDefault();
if (dbAction == null)
{
if (tdAcc == null)
{
tdAcc = DbContext.trade_accumulator_option.AsNoTracking().FirstOrDefault(n => n.TradeId == td.id);
}
changeData = TradeAccumulatorHelper.GetDefaultChangeData<TradeAccumulatorChangeDataEx>(valueDate, td, tdAcc);
var list = ParseKoObservation(changeData.KOObservationDates, changeData.KOObservationSettleDates, valueDate);
(string KOObservationDates, string KOObservationSettleDates) = BuildKOObservationDates(list, false);
changeData.KOObservationDates = KOObservationDates;
changeData.KOObservationSettleDates = KOObservationSettleDates;
}
else
{
changeData = JsonHelper.Deserialize<TradeAccumulatorChangeDataEx>(dbAction.ActionData);
changeData.ValueDate = valueDate.OtcFormatDate();
if (dbAction.ValueDate < valueDate)
{
var list = ParseKoObservation(changeData.KOObservationDates, changeData.KOObservationSettleDates, valueDate);
(string KOObservationDates, string KOObservationSettleDates) = BuildKOObservationDates(list, false);
changeData.KOObservationDates = KOObservationDates;
changeData.KOObservationSettleDates = KOObservationSettleDates;
}
}
var nextDate = dbActions.Where(x => x.ValueDate > valueDate).LastOrDefault()?.ValueDate;
changeData.NextDate = (nextDate ?? td.ExerciseDate.Value).OtcFormatDate();
if (!nextDate.HasValue)
{
nextDate = td.ExerciseDate.Value.AddDays(1);
}
changeData.HasObCheck = DbContext.autocall_observation.Any(n => n.TradeId == td.id && n.EndDate >= valueDate && n.EndDate < nextDate);
return changeData;
}
#region----观察数据静态方法处理----
//解析敲出观察数据(包含startDate但不包含endDate)
private static List<KoObservationItem> ParseKoObservation(string KOObservationDates, string KOObservationSettleDates
, DateTime startDate, DateTime? endDate = null)
{
var customObservDates = QdpHelper.ParseAutocallCustomizedInfo(KOObservationDates);
var obDates = customObservDates.Item1;
if (!endDate.HasValue)
{
endDate = DateTime.MaxValue;
}
if (obDates != null)
{
var list = new List<KoObservationItem>(obDates.Length);
var customKOBarriers = customObservDates.Item2;
var customCoupons = customObservDates.Item3;
var settleDates = QdpHelper.ParseObservationDate(KOObservationSettleDates);
for (var i = 0; i < obDates.Length; i++)
{
var dt = obDates[i].DateTime;
if (dt >= startDate && dt < endDate)
{
var item = new KoObservationItem
{
date = dt
};
if (customKOBarriers != null && customKOBarriers.Length > i)
{
item.barrier = customKOBarriers[i];
}
if (customCoupons != null && customCoupons.Length > i)
{
item.coupon = customCoupons[i];
}
if (settleDates != null && settleDates.Length > i)
{
item.settleDate = settleDates[i].DateTime;
}
list.Add(item);
}
}
return list;
}
return new List<KoObservationItem>();
}
/// <summary>
/// 获取累计期权平仓定价日的观察日期
/// </summary>
/// <param name="kOObservationDates">观察日期</param>
/// <param name="valueDate">定价日期或平仓日期</param>
/// <param name="tradeDate">交易日期</param>
/// <param name="exerciseDate">到期日期</param>
public int GetAccTradeUnwindDayCount(string kOObservationDates, DateTime? valueDate, DateTime? tradeDate, DateTime? exerciseDate)
{
var dayCount = 0;
var startDate = (valueDate ?? tradeDate).GetValueOrDefault();
if (kOObservationDates != null)
{
var koModel = QdpHelper.ParseAutocallCustomizedInfo(kOObservationDates);
if (valueDate != null)
{
dayCount = koModel.Item1.Count(O => O.DateTime >= valueDate);
}
else
{
dayCount = koModel.Item1.Count(O => O.DateTime >= startDate);
}
}
else
{
if (startDate == tradeDate)//默认观察日列表不包括首日观察
{
startDate = startDate.AddDays(1);
}
dayCount = QdpCalendarHelper.GetWorkDayCount(startDate, exerciseDate.GetValueOrDefault()) + 1;
}
return dayCount;
}
/// <summary>
/// 组合观察数据项到字符串
/// </summary>
/// <param name="itemList"></param>
/// <param name="useObDateAsSettleDateIfEmpty">如果结算日为空是否使用观察日</param>
private static (string KOObservationDates, string KOObservationSettleDates) BuildKOObservationDates(List<KoObservationItem> itemList
, bool useObDateAsSettleDateIfEmpty)
{
if (itemList == null || !itemList.Any())
{
return (string.Empty, string.Empty);
}
var sb_obdates = new StringBuilder(512);
var sb_barrier = new StringBuilder(512);
var sb_coupon = new StringBuilder(512);
var sb_stdates = new StringBuilder(512);
foreach (var item in itemList)
{
sb_obdates.Append(item.date.ToString("yyyy-MM-dd")).Append(',');
if (item.barrier.HasValue)
{
sb_barrier.Append(item.barrier).Append(',');
}
if (item.coupon.HasValue)
{
sb_coupon.Append(item.coupon).Append(',');
}
if (item.settleDate.HasValue)
{
sb_stdates.Append(item.settleDate?.ToString("yyyy-MM-dd")).Append(',');
}
else if (useObDateAsSettleDateIfEmpty)
{
sb_stdates.Append(item.date.ToString("yyyy-MM-dd")).Append(',');
}
}
sb_obdates.Remove(sb_obdates.Length - 1, 1).Append(';');
if (sb_barrier.Length > 0)
{
sb_barrier.Remove(sb_barrier.Length - 1, 1);
sb_obdates.Append(sb_barrier.ToString()).Append(';');
}
if (sb_coupon.Length > 0)
{
sb_coupon.Remove(sb_coupon.Length - 1, 1);
sb_obdates.Append(sb_coupon.ToString());
}
if (sb_stdates.Length > 0)
{
sb_stdates.Remove(sb_stdates.Length - 1, 1);
}
return (sb_obdates.ToString(), sb_stdates.ToString());
}
#endregion
}
public class TradeAccumulatorChangeData
{
/// <summary>
/// 执行日期
/// </summary>
public string ValueDate { get; set; }
/// <summary>
/// 标的代码
/// </summary>
public string UnderlyingCode { get; set; }
/// <summary>
/// 执行价格
/// </summary>
public double Strike { get; set; }
/// <summary>
/// 敲出障碍价格
/// </summary>
public double? KOBarrier { get; set; }
/// <summary>
/// 观察日期
/// </summary>
public string KOObservationDates { get; set; }
/// <summary>
/// 结算日期
/// </summary>
public string KOObservationSettleDates { get; set; }
/// <summary>
/// 观察频率
/// </summary>
public string KOObservation { get; set; }
public TradeAccumulatorChangeData Clone()
{
return (TradeAccumulatorChangeData)MemberwiseClone();
}
public override string ToString()
{
return $"{ValueDate:yyyy-MM-dd}--{UnderlyingCode}";
}
}
public class TradeAccumulatorChangeDataEx : TradeAccumulatorChangeData
{
/// <summary>
/// 下一日
/// </summary>
public string NextDate { get; set; }
/// <summary>
/// 存在累计收益
/// </summary>
public bool HasObCheck { get; set; }
}
/// <summary>
/// 换月操作结果数据
/// </summary>
public class TradeAccumulatorChangeResult
{
/// <summary>
/// 是否交易数据被更新
/// </summary>
public bool TradeUpdated { get; set; }
/// <summary>
/// 产生的或受影响的actions
/// </summary>
public IEnumerable<TradeAction> Actions { get; set; }
}
}