Files
zszq-trs/YLErpDAL/Modules/EodModule/SettlementModule/EodForwardMarginSettlement.cs
T
2024-05-09 14:06:26 +08:00

232 lines
8.3 KiB
C#

using YLErp.DBModels.Enums;
namespace YLErp.Modules.EodModule.SettlementModule
{
/// <summary>
/// 远期预付金收盘结算
/// </summary>
class EodForwardMarginSettlement : EodSettleServiceBaseV2
{
public const string Step = "远期预付金结算";
public EodForwardMarginSettlement(EodSettlementContextV2 context) : base(context)
{
}
/// <summary>
/// 远期预付金收盘结算
/// </summary>
public void Execute()
{
var settleDate = _context.SettleDate;
//计息天数--如果大于1时判断中间的日期是否遗漏了收盘
var interestDays = (settleDate - _context.PreSettleDate).Days;
//--------------------------------------------
// 删除旧数据
//--------------------------------------------
if (_context.ClienIds!=null&& _context.ClienIds.Any())
{
var forwardTradeId = _context.OtcTrades.Where(t => t.TradeType == "远期").Where(l => _context.ClienIds.Contains(l.ClientId)).Select(l => l.id);
var forwardTradeIdStr = string.Join(",", forwardTradeId);
if (!string.IsNullOrEmpty(forwardTradeIdStr))
{
DbContext.BulkDelete<eod_forward_margin>($" TradeId in({forwardTradeIdStr}) and ValueDate='{settleDate:yyyy-MM-dd}'");
}
}
else
{
DbContext.BulkDelete<eod_forward_margin>($"ValueDate='{settleDate:yyyy-MM-dd}'");
}
//--------------------------------------------
// 基础数据查询 -- 构建查询时需要考虑历史收盘
// 最后一天为特殊场景,需要考虑到和上一交易日中间有假期的情况
//--------------------------------------------
var tradeInfos = _context.OtcTrades.Where(t => t.TradeType == "远期").Select(GetTradeInfo).ToArray();
_context.CheckCanceled();
if (!tradeInfos.Any())
{
return;
}
var eodLastDic = DbContext.eod_forward_margin.Where(a => a.ValueDate == _context.PreSettleDate).ToDictionary(n => n.TradeId);
foreach (var td in tradeInfos)
{
eodLastDic.TryGetValue(td.TradeId, out var lastMargin);
if (lastMargin == null && _context.PreSettleDate >= td.TradeDate)
{
_context.RaiseError(Step, $"交易'{td.TradeNumber}'缺少上一交易日({_context.PreSettleDate.OtcFormatDate()})结算数据");
}
var margin = CalcMargin(td, lastMargin, interestDays);
DbContext.eod_forward_margin.Add(margin);
}
DbContext.SaveChanges(_context.CancellationToken);
}
private eod_forward_margin CalcMargin(TradeInfo td, eod_forward_margin lastMargin, int interestDays)
{
var margin = new eod_forward_margin
{
AnnualRate = td.AnnualMarginRate,
Notional = td.Notional,
TradeId = td.TradeId,
ValueDate = _context.SettleDate,
MarginRate = 0,
SettlePrice = 0,
MarginSum = 0,
CloseMarginSum = 0,
InterestDays = 0,
MarginCost = 0
};
double? settlePrice = null;
switch (td.MarginType)
{
case MarginTypeEnum.NONE:
margin.MarginRate = 0;
break;
case MarginTypeEnum.FIXED:
margin.MarginRate = td.PositionMarginRate;
settlePrice = td.SpotPrice;
break;
case MarginTypeEnum.FLOAT:
margin.MarginRate = td.MarginRate;
break;
default:
var um = UnderlyingDataProvider.GetUnderlying(td.UnderlyingCode);
if (um == null)
{
_context.RaiseError(Step, $"交易'{td.TradeNumber}'未找到标的数据:{td.UnderlyingCode}");
}
margin.MarginRate = um.MarginRate ?? 0;
break;
case MarginTypeEnum.FLOATP:
margin.MarginRate = td.PositionMarginRate;
break;
}
if (settlePrice == null)
{
if (_context.GetEodPriceProvider().TryGetPrice(td.UnderlyingCode, SettlementTypeEnum.ClosePrice, out var price))
{
settlePrice = price;
}
else
{
_context.RaiseError(Step, $"交易'{td.TradeNumber}'未找到标的结算价:{td.UnderlyingCode}");
}
}
margin.SettlePrice = settlePrice.Value;
if (lastMargin != null)
{
margin.MarginSum = lastMargin.MarginSum;
margin.CloseMarginSum = lastMargin.CloseMarginSum;
//首先结算假期日的预付金成本
if (interestDays > 1)
{
margin.InterestDays = interestDays - 1;
//使用上一交易日结算数据重算(因为上一交易日也可能会有假期日的计息)
margin.MarginCost = lastMargin.Notional * settlePrice.Value * lastMargin.MarginRate * lastMargin.AnnualRate * margin.InterestDays / 365;
}
}
//当前交易日持仓预付金成本结算
if (td.IsCompleted)
{
margin.Notional = 0;
}
else
{
margin.InterestDays += 1;
if (lastMargin != null && lastMargin.Notional > td.Notional)
{
//算法:昨日预付金累计+(昨日持仓预付金累计=昨日预付金累计-昨日平仓预付金累计)*(今日平仓数量=昨日持仓份额-今日持仓份额)/昨日持仓份额
margin.CloseMarginSum += (lastMargin.MarginSum - lastMargin.CloseMarginSum) * (lastMargin.Notional - td.Notional) / lastMargin.Notional;
}
margin.MarginCost += margin.Notional * settlePrice.Value * margin.MarginRate * margin.AnnualRate / 365;
}
margin.MarginSum += margin.MarginCost;
return margin;
}
class TradeInfo
{
public int TradeId { get; set; }
public string TradeNumber { get; set; }
public DateTime TradeDate { get; set; }
public double Notional { get; set; }
public MarginTypeEnum MarginType { get; set; }
public double AnnualMarginRate { get; set; }
public string UnderlyingCode { get; set; }
public double SpotPrice { get; set; }
public double MarginRate { get; set; }
public double PositionMarginRate { get; set; }
public bool IsCompleted { get; set; }
public override string ToString()
{
return $"{UnderlyingCode}--{TradeId}--{TradeNumber}";
}
}
private TradeInfo GetTradeInfo(trade td)
{
if (td.TradeDate == null)
{
_context.RaiseError(Step, "缺少交易日期:" + td.TradeNumber);
return null;
}
if (td.trade_forward == null)
{
_context.RaiseError(Step, "缺少远期交易数据:" + td.TradeNumber);
return null;
}
return new TradeInfo
{
TradeId = td.id,
TradeNumber = td.TradeNumber,
TradeDate = td.TradeDate.Value,
MarginType = td.MarginType,
Notional = td.Notional,
AnnualMarginRate = td.trade_forward.AnnualMarginRate,
UnderlyingCode = td.UnderlyingCode,
SpotPrice = td.SpotPrice ?? 0,
MarginRate = td.MarginRate,
PositionMarginRate = td.PositionMarginRate,
IsCompleted = ConsTrade.TradeCompleteStatus.Contains(td.TradeStatus)
};
}
}
}