674 lines
31 KiB
C#
674 lines
31 KiB
C#
using Qdp.Foundation.Implementations;
|
|
using YLErp.Abstract.DataProviders;
|
|
using YLErp.BLL;
|
|
using YLErp.Commons;
|
|
using YLErp.DBModels.Enums;
|
|
using YLErp.DBModels;
|
|
using YLErp.Modules.DataProviderModule;
|
|
using YLErp.Modules.EodModule;
|
|
using YLErp.QdpModule;
|
|
using NPOI.SS.Formula.Functions;
|
|
|
|
namespace YLErp.Modules.CalculationModule
|
|
{
|
|
/// <summary>
|
|
/// 收益互换计算服务
|
|
/// </summary>
|
|
public class PayoffSwapCalcService
|
|
{
|
|
/// <summary>
|
|
/// 因为互换涉及到多标的所以需要确保所有标的都可以找到价格
|
|
/// </summary>
|
|
private static IAggregatePriceProvider GetAutoPriceProvider(DateTime valueDate, IPriceProvider priceProvider, bool isEodSettlement)
|
|
{
|
|
if (priceProvider is IAggregatePriceProvider)
|
|
{
|
|
return (IAggregatePriceProvider)priceProvider;
|
|
}
|
|
|
|
if (isEodSettlement)
|
|
{
|
|
return new AggregatePriceProvider(priceProvider,
|
|
new EodPriceProvider(valueDate).GetPriceProvider(priceProvider is IEodPriceProviderWrap wrap ? wrap.SettlementType : SettlementTypeEnum.ClosePrice));
|
|
}
|
|
|
|
return new AggregatePriceProvider(priceProvider, DataCacheProvider.GetUnderlyingDataSource());
|
|
}
|
|
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
public static TradeValueResult CalcValue(int tradeId, DateTime valueDate, IPriceProvider priceProvider, bool isEodSettlement)
|
|
{
|
|
using (var db = DbContextFactory.GetYLDbContext())
|
|
{
|
|
var trade = db.trade.Find(tradeId);
|
|
if (trade == null)
|
|
{
|
|
return new TradeValueResult(false)
|
|
{
|
|
TradeId = tradeId,
|
|
FailReason = TradeValueFailReason.missingTrade,
|
|
ErrorMessage = "[收益互换]没有找到交易数据,tradeId:" + tradeId
|
|
};
|
|
}
|
|
return CalcValue(trade, valueDate, priceProvider, isEodSettlement);
|
|
}
|
|
}
|
|
|
|
public static TradeValueResult CalcValue(OtcTradeBase trade, DateTime valueDate, IPriceProvider priceProvider, bool isEodSettlement)
|
|
{
|
|
return CalcValueSingle(trade, valueDate, priceProvider, isEodSettlement);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 互换收益PV计算 普通
|
|
/// </summary>
|
|
public static TradeValueResult CalcValueSingle(OtcTradeBase trade, DateTime valueDate, IPriceProvider priceProvider, bool isEodSettlement, double spotPrice = double.NaN)
|
|
{
|
|
if (trade is null)
|
|
{
|
|
throw new ArgumentNullException(nameof(trade));
|
|
}
|
|
|
|
if(double.IsNaN(spotPrice))
|
|
{
|
|
//因为互换涉及到多标的所以需要确保所有标的都可以找到价格
|
|
if (trade.StructureType== "多空组合")
|
|
{
|
|
spotPrice = 0;
|
|
}
|
|
else
|
|
{
|
|
priceProvider = GetAutoPriceProvider(valueDate, priceProvider, isEodSettlement);
|
|
if (!priceProvider.TryGetPrice(trade.UnderlyingCode, out spotPrice))
|
|
{
|
|
spotPrice = DataCacheProvider.GetUnderlyingDataSource().GetPrice(trade.UnderlyingCode);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
using (var db = DbContextFactory.GetYLDbContext())
|
|
{
|
|
var client = DataCacheProvider.GetClientDataSource().GetData(trade.ClientId);
|
|
var rate = new EodCurrencyRateService(OptUserInfo.SystemUser).GetCurrencyRate(trade.QuoteCurrency, trade.SettlementCurrency, valueDate, seekPreday: !isEodSettlement);
|
|
var rateTradeDate = new EodCurrencyRateService(OptUserInfo.SystemUser).GetCurrencyRate(trade.QuoteCurrency, trade.SettlementCurrency, trade.TradeDate.Value, seekPreday: !isEodSettlement);
|
|
var lastEodSwap = GetEodSwapData(trade, db);
|
|
var pv = lastEodSwap.PostionValue;
|
|
var clientCashOut= db.ClientCashInCashOut.FirstOrDefault(x=>x.Action== "系统操作-期权费"&&x.TradeId== trade.id&&x.HappenDate<= valueDate) ;
|
|
var credit = db.credit.FirstOrDefault(x => x.CreditStartDate <= valueDate && x.CreditDeadLine >= valueDate && x.ClientId == trade.ClientId && x.ProcessStatus == "已审批");
|
|
var hasCredit = credit != null && credit.PFECredit > 0;
|
|
|
|
client_variety_marginrate clientVarietyMarginRate = new client_variety_marginrate
|
|
{
|
|
ClientId = trade.ClientId,
|
|
ClientName = trade.ClientName,
|
|
HighMarginRate = 1,
|
|
LowMarginRate = 1,
|
|
ValueDate = valueDate,
|
|
};
|
|
var OptionValue = new TradeValueResult
|
|
{
|
|
TradeId = trade.id,
|
|
Pv =Convert.ToDouble(pv),
|
|
ExtendInfo = new TradeValueResultExtend()
|
|
{
|
|
QuoteFloatingWinLoss =Convert.ToDouble(lastEodSwap.FloatingPnL),
|
|
FloatingWinLoss = Convert.ToDouble(lastEodSwap.FloatingPnL) * rate,
|
|
RealPnl = Convert.ToDouble(lastEodSwap.RealizedPnL) * rate,
|
|
QuoteCommission = clientCashOut?.Money??0,
|
|
Commission = (clientCashOut?.Money ?? 0) * rate,
|
|
QuoteAnnualFee = 0,
|
|
AnnualFee = 0,
|
|
QuotePv = Convert.ToDouble(pv),
|
|
QuoteIM = client.BoundSide == BoundSideEnum.南向 ? (trade.Notional * spotPrice * clientVarietyMarginRate.LowMarginRate) : (trade.StockEqvNotional * (hasCredit ? clientVarietyMarginRate.LowMarginRate : clientVarietyMarginRate.HighMarginRate)),
|
|
IM = (client.BoundSide == BoundSideEnum.南向 ? (trade.Notional * spotPrice * clientVarietyMarginRate.LowMarginRate) : (trade.StockEqvNotional * (hasCredit ? clientVarietyMarginRate.LowMarginRate : clientVarietyMarginRate.HighMarginRate))) * rate,
|
|
QuotePFE = client.BoundSide == BoundSideEnum.南向 ? trade.Notional * spotPrice * clientVarietyMarginRate.HighMarginRate : 0,
|
|
PFE = client.BoundSide == BoundSideEnum.南向 ? trade.Notional * spotPrice * clientVarietyMarginRate.HighMarginRate * rate : 0
|
|
},
|
|
RoundedPv = Convert.ToDouble(pv),
|
|
Delta = (lastEodSwap.MarketValueLong>0 ? 1 : -1) * trade.Notional,
|
|
Gamma = 0,
|
|
Vega = 0,
|
|
TradingDayTheta = 0,
|
|
CalendarDayTheta = 0,
|
|
Rho = 0,
|
|
DeltaCash = (lastEodSwap.MarketValueLong > 0 ? spotPrice : -spotPrice) * trade.Notional,
|
|
GammaCash = 0,
|
|
SpotPrice= spotPrice,
|
|
DV01= Convert.ToDouble(lastEodSwap.DV01)
|
|
};
|
|
|
|
return OptionValue;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 构建eodswap
|
|
/// </summary>
|
|
/// <param name="tradeId"></param>
|
|
/// <param name="db"></param>
|
|
/// <returns></returns>
|
|
private static eod_swap GetEodSwapData(OtcTradeBase trade, YLContext db)
|
|
{
|
|
var eodSwap = new eod_swap();
|
|
var positions = db.swap_position.Where(x => x.PosiQuantity > 0 && !x.IsInitial && x.SwapTradeId == trade.id).ToList();
|
|
eodSwap.SwapTradeId = trade.id;
|
|
eodSwap.NotionalValueLong = positions.Where(x => x.PositionType == (int)PositionTypeFlag.Long).Sum(s => s.PosiNotionalValue);
|
|
eodSwap.NotionalValueShort = positions.Where(x => x.PositionType == (int)PositionTypeFlag.Short).Sum(s => s.PosiNotionalValue);
|
|
eodSwap.NotionalValue = eodSwap.NotionalValueLong + eodSwap.NotionalValueShort;
|
|
eodSwap.DV01 = 0;
|
|
var lastEod = db.eod_swap.Where(x => x.SwapTradeId == trade.id).OrderByDescending(o => o.ValueDate).FirstOrDefault();
|
|
eodSwap.RealizedPnL = lastEod?.RealizedPnL ?? 0;
|
|
eodSwap.InterestPnL = lastEod?.InterestPnL ?? 0;
|
|
foreach (var item in positions)
|
|
{
|
|
decimal shortRatio = item.PositionType == (int)PositionTypeFlag.Long ? 1 : -1;//多空方向
|
|
int directionRatio = item.PosiDirection == (int)SwapDirectionEnum.收取 ? 1 : -1;
|
|
var pv = item.PosiQuantity * shortRatio * item.ContractSize;
|
|
var pvNoPrice = item.PosiQuantity * item.ContractSize;
|
|
decimal vobp = 0;
|
|
var data = DataCacheProvider.GetUnderlyingDataSource().GetData(item.UnderlyingCode);
|
|
if (data != null)
|
|
{
|
|
if (data.IsBond())
|
|
{
|
|
var bondPrice = EodPriceQueryService.GetBondPrice(valuedateBLL.ValueDate, data.UnderlyingCode);
|
|
vobp = bondPrice?.Vobp ?? 0;
|
|
var price = Convert.ToDecimal(bondPrice?.ClosePrice??0);
|
|
eodSwap.FloatingPnL = (price - item.PosiGrossPrice) * item.PosiQuantity * item.ContractSize * shortRatio * directionRatio;
|
|
}
|
|
}
|
|
if (shortRatio > 0)
|
|
{
|
|
eodSwap.MarketValueLong += pv;
|
|
}
|
|
else
|
|
{
|
|
eodSwap.MarketValueShort += pv;
|
|
}
|
|
eodSwap.NotionalValue += pvNoPrice;
|
|
eodSwap.DV01 += pvNoPrice * vobp * shortRatio * directionRatio * 0.01m;
|
|
}
|
|
eodSwap.PostionValue = eodSwap.InterestPnL + eodSwap.FloatingPnL;
|
|
return eodSwap;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 获取互换固定收益PV
|
|
/// </summary>
|
|
/// <param name="trades"></param>
|
|
/// <param name="valueDate"></param>
|
|
/// <returns></returns>
|
|
public static Dictionary<int, double> GetFixedInterestRatePV(List<int> tradeIds, DateTime valueDate)
|
|
{
|
|
var result = new Dictionary<int, double>();
|
|
if (tradeIds == null || tradeIds.Count == 0)
|
|
{
|
|
return result;
|
|
}
|
|
List<trade_swap> tradeSwapList = null;
|
|
List<trade_cash> tradeCashList = null;
|
|
List<trade_cash_swap> tradeCashSwapList = null;
|
|
List<trade> tradeList = null;
|
|
List<eod_trade> eodTradeList = null;
|
|
using (var db = DbContextFactory.GetYLDbContext())
|
|
{
|
|
//tradeList = db.trade.AsNoTracking().Where(p => tradeIds.Contains(p.id)).ToList();
|
|
eodTradeList = db.eod_trade.AsNoTracking().Where(p => p.ValueDate == valueDate && tradeIds.Contains(p.TradeId)).ToList();
|
|
tradeSwapList = db.trade_swap.AsNoTracking().Where(p => tradeIds.Contains(p.TradeId)).ToList();
|
|
tradeCashList = db.trade_cash.AsNoTracking().Where(y => tradeIds.Contains(y.TradeId) && y.Action == "系统操作-互换" && y.ValidState != "InValid" && !y.IsDeleted && y.ValueDate <= valueDate).ToList();
|
|
tradeCashSwapList = db.trade_cash_swap.AsNoTracking().Where(p => tradeIds.Contains(p.TradeId)).ToList();
|
|
}
|
|
|
|
if (tradeSwapList == null)
|
|
{
|
|
tradeSwapList = new List<trade_swap>();
|
|
}
|
|
if (tradeCashList == null)
|
|
{
|
|
tradeCashList = new List<trade_cash>();
|
|
}
|
|
if (tradeCashSwapList == null)
|
|
{
|
|
tradeCashSwapList = new List<trade_cash_swap>();
|
|
}
|
|
if (eodTradeList != null && eodTradeList.Count > 0)
|
|
{
|
|
tradeList = eodTradeList.Select(p => p.trade).ToList();
|
|
}
|
|
if (tradeList == null)
|
|
{
|
|
tradeList = new List<trade>();
|
|
}
|
|
if (tradeList.Count > 0)
|
|
{
|
|
foreach (var trade in tradeList)
|
|
{
|
|
var tradeSwap = tradeSwapList.FirstOrDefault(d => d.TradeId == trade.id);
|
|
if (tradeSwap == null)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
var tradeCashs = tradeCashList.Where(y => y.TradeId == trade.id && y.Action == "系统操作-互换" && y.ValidState != "InValid" && !y.IsDeleted && y.ValueDate <= valueDate);
|
|
var tradeCashIds = tradeCashs.Select(x => x.id);
|
|
var tradeCash = tradeCashs.OrderByDescending(y => y.id).FirstOrDefault();
|
|
var cashSwaps = tradeCashSwapList.Where(x => x.TradeId == trade.id && tradeCashIds.Contains(x.TradeCashId)).ToArray();
|
|
//var tradeCashSwap = tradeCash != null ? cashSwaps.FirstOrDefault(x => x.TradeCashId == tradeCash.id) : null;
|
|
//取最后一次手动收益;
|
|
var lastManualCashSwap = cashSwaps.OrderByDescending(o => o.StartDate).FirstOrDefault(x => !x.IsAuto);
|
|
var lastManualCash = lastManualCashSwap != null ? tradeCashs.FirstOrDefault(x => x.id == lastManualCashSwap.TradeCashId) : null;
|
|
DateTime endDate;
|
|
double fixAmount = 0;
|
|
if (!tradeSwap.IsGetFloatingProfit)
|
|
{
|
|
var preSwapDate = GetSwapRateStartDate(trade, tradeSwap, valueDate, tradeCash, lastManualCash, tradeSwap.IsGetFloatingProfit, out endDate);
|
|
var extraAmountGet = GetExtraAmountBySwapRate(trade.ClientId, trade.TradeDate, tradeSwap.GetSwapTimeAndRate, preSwapDate, endDate, tradeSwap.AnnualDays ?? 0, trade.StockEqvNotional);
|
|
fixAmount += extraAmountGet;
|
|
}
|
|
if (!tradeSwap.IsPayFloatingProfit)
|
|
{
|
|
var preSwapDate = GetSwapRateStartDate(trade, tradeSwap, valueDate, tradeCash, lastManualCash, tradeSwap.IsPayFloatingProfit, out endDate);
|
|
var extraAmountPay = GetExtraAmountBySwapRate(trade.ClientId, trade.TradeDate, tradeSwap.PaySwapTimeAndRate, preSwapDate, endDate, tradeSwap.AnnualDays ?? 0, trade.StockEqvNotional);
|
|
fixAmount -= extraAmountPay;
|
|
}
|
|
result.Add(trade.id, fixAmount);
|
|
}
|
|
}
|
|
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// 多空组合PV计算 子交易计算模式
|
|
/// </summary>
|
|
public static TradeValueResult CalcValue(OtcTradeBase trade, trade_swap trade_swap, List<trade_swap_detail> trade_swap_details, DateTime valueDate, IPriceProvider priceProvider, bool isEodSettlement)
|
|
{
|
|
if (trade_swap.SwapType == "多空组合")
|
|
{
|
|
using (var db = DbContextFactory.GetYLDbContext())
|
|
{
|
|
var tradeIds = trade_swap_details.Select(t => t.ChildTradeId).ToList();
|
|
var subTradeList = db.trade.Where(t => tradeIds.Contains(t.id)).ToList();
|
|
var resultList = new List<TradeValueResult>();
|
|
foreach (var subTrade in subTradeList)
|
|
{
|
|
var price = DataCacheProvider.GetUnderlyingDataSource().GetPrice(subTrade.UnderlyingCode);
|
|
var subResult = CalcValue(subTrade, valueDate, priceProvider, isEodSettlement);
|
|
resultList.Add(subResult);
|
|
}
|
|
|
|
var OptionValue = new TradeValueResult
|
|
{
|
|
TradeId = trade.id,
|
|
Pv = 0,
|
|
ExtendInfo = new TradeValueResultExtend()
|
|
{
|
|
QuoteFloatingWinLoss = 0,
|
|
FloatingWinLoss = 0,
|
|
QuoteCommission = 0,
|
|
Commission = 0,
|
|
QuoteAnnualFee = 0,
|
|
AnnualFee = 0,
|
|
QuotePv = 0,
|
|
QuoteIM = 0,
|
|
IM = 0,
|
|
QuotePFE = 0,
|
|
PFE = 0
|
|
},
|
|
RoundedPv = 0,
|
|
Delta = 0,
|
|
Gamma = 0,
|
|
Vega = 0,
|
|
TradingDayTheta = 0,
|
|
CalendarDayTheta = 0,
|
|
Rho = 0,
|
|
DeltaCash = 0,
|
|
GammaCash = 0
|
|
};
|
|
|
|
foreach (var valueResult in resultList)
|
|
{
|
|
OptionValue.Pv += valueResult.Pv;
|
|
OptionValue.ExtendInfo.QuoteFloatingWinLoss += valueResult.ExtendInfo.QuoteFloatingWinLoss;
|
|
OptionValue.ExtendInfo.FloatingWinLoss += valueResult.ExtendInfo.FloatingWinLoss;
|
|
OptionValue.ExtendInfo.QuoteCommission += valueResult.ExtendInfo.QuoteCommission;
|
|
OptionValue.ExtendInfo.Commission += valueResult.ExtendInfo.Commission;
|
|
OptionValue.ExtendInfo.QuoteAnnualFee += valueResult.ExtendInfo.QuoteAnnualFee;
|
|
OptionValue.ExtendInfo.AnnualFee += valueResult.ExtendInfo.AnnualFee;
|
|
OptionValue.ExtendInfo.QuotePv += valueResult.ExtendInfo.QuotePv;
|
|
OptionValue.ExtendInfo.QuoteIM += valueResult.ExtendInfo.QuoteIM;
|
|
OptionValue.ExtendInfo.IM += valueResult.ExtendInfo.IM;
|
|
OptionValue.ExtendInfo.QuotePFE += valueResult.ExtendInfo.QuotePFE;
|
|
OptionValue.ExtendInfo.PFE += valueResult.ExtendInfo.PFE;
|
|
OptionValue.RoundedPv += valueResult.RoundedPv;
|
|
OptionValue.DeltaCash += valueResult.DeltaCash;
|
|
}
|
|
return OptionValue;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return CalcValue(trade,valueDate, priceProvider, isEodSettlement);
|
|
}
|
|
}
|
|
/// <summary>
|
|
/// 计算互换利息
|
|
/// </summary>
|
|
public static double GetExtraAmountBySwapRate(int clientId, DateTime? tradeDate, string timeRate, DateTime startDate, DateTime valueDate, int annualDays, double stockEqvNotional)
|
|
{
|
|
if (valueDate == tradeDate && PS.Config.Company == Configuration.CompanyEnum.中金)
|
|
{
|
|
var client = DataCacheProvider.GetClientDataSource().GetData(clientId);
|
|
if (client.BoundSide == BoundSideEnum.北向)
|
|
{
|
|
startDate = startDate.AddDays(-1);
|
|
}
|
|
}
|
|
|
|
double extraAmountGet = 0;
|
|
var swapDates = GetSwapDatesBetween(timeRate, startDate, valueDate);
|
|
if (swapDates != null && swapDates.Any())
|
|
{
|
|
swapDates.ForEach(x =>
|
|
{
|
|
var itemDays = (x.DateTime - startDate).Days;
|
|
var itemRate = GetSwapRateByDate(timeRate, x.DateTime);
|
|
extraAmountGet += stockEqvNotional * itemRate * ((double)itemDays / annualDays);
|
|
startDate = x.DateTime;
|
|
});
|
|
}
|
|
|
|
if (startDate < valueDate)
|
|
{
|
|
var latestDays = (valueDate - startDate).Days;
|
|
var LatestRate = GetSwapRateByDate(timeRate, valueDate);
|
|
extraAmountGet += stockEqvNotional * LatestRate * ((double)latestDays / annualDays);
|
|
}
|
|
|
|
return extraAmountGet.Normalize().FormatValue(2);
|
|
}
|
|
/// <summary>
|
|
/// 获取计息开始日期,结束日期
|
|
/// </summary>
|
|
/// <param name="td"></param>
|
|
/// <param name="valueDate"></param>
|
|
/// <param name="tradeCash"></param>
|
|
/// <param name="lastManualCashSwap"></param>
|
|
/// <param name="floating"></param>
|
|
/// <param name="endDate"></param>
|
|
/// <returns></returns>
|
|
public static DateTime GetSwapRateStartDate(OtcTradeBase td, trade_swap tradeSwap, DateTime valueDate, trade_cash tradeCash, trade_cash lastManualCash, bool floating, out DateTime endDate)
|
|
{
|
|
var tradeStartDate = PS.Config.Company == Configuration.CompanyEnum.中金 ? td.TradeDate.Value : td.StartDate.Value;
|
|
var calcFirst = tradeSwap.RateCalcMode.StartsWith("1");//算头
|
|
var calcLast = tradeSwap.RateCalcMode.EndsWith("1");//算尾
|
|
DateTime startDate = calcFirst ? tradeStartDate.AddDays(-1) : tradeStartDate;
|
|
//如果valueDate超过了到期日,利息以到期日来计算
|
|
endDate = valueDate > td.ExerciseDate ? td.ExerciseDate.Value : valueDate;
|
|
//是否算尾
|
|
if (td.ExerciseDate == endDate && !calcLast)
|
|
{
|
|
endDate = endDate.AddDays(-1);
|
|
}
|
|
if (floating)
|
|
{
|
|
if(lastManualCash != null)
|
|
{
|
|
startDate = lastManualCash.ValueDate;
|
|
}
|
|
}
|
|
else if (tradeCash != null)
|
|
{
|
|
startDate = tradeCash.ValueDate;
|
|
}
|
|
|
|
return startDate;
|
|
}
|
|
/// <summary>
|
|
/// 获取计息开始日期,结束日期
|
|
/// </summary>
|
|
/// <param name="td"></param>
|
|
/// <param name="valueDate"></param>
|
|
/// <param name="tradeCash"></param>
|
|
/// <param name="lastManualCashSwap"></param>
|
|
/// <param name="floating"></param>
|
|
/// <param name="endDate"></param>
|
|
/// <returns></returns>
|
|
public static DateTime GetSwapRateStartDatePre(OtcTradeBase td, trade_swap tradeSwap, DateTime valueDate, trade_cash tradeCash, trade_cash_pre lastManualCash, bool floating, out DateTime endDate)
|
|
{
|
|
var tradeStartDate = PS.Config.Company == Configuration.CompanyEnum.中金 ? td.TradeDate.Value : td.StartDate.Value;
|
|
var calcFirst = tradeSwap.RateCalcMode.StartsWith("1");//算头
|
|
var calcLast = tradeSwap.RateCalcMode.EndsWith("1");//算尾
|
|
DateTime startDate = calcFirst ? tradeStartDate.AddDays(-1) : tradeStartDate;
|
|
endDate = valueDate;
|
|
if (td.ExerciseDate == endDate && !calcLast)
|
|
{
|
|
endDate = endDate.AddDays(-1);
|
|
}
|
|
if (floating)
|
|
{
|
|
if (lastManualCash != null)
|
|
{
|
|
startDate = lastManualCash.ValueDate;
|
|
}
|
|
}
|
|
else if (tradeCash != null)
|
|
{
|
|
startDate = tradeCash.ValueDate;
|
|
}
|
|
|
|
return startDate;
|
|
}
|
|
/// <summary>
|
|
/// 计算互换手续费
|
|
/// </summary>
|
|
/// <param name="tradePosition"></param>
|
|
/// <param name="tradeImport"></param>
|
|
/// <param name="tradeCash"></param>
|
|
/// <param name="isForGet"></param>
|
|
/// <returns></returns>
|
|
public static double GetCostFee(trade tradePosition, trade tradeImport, trade_cash tradeCash, bool isForGet, bool isOpenFee)
|
|
{
|
|
if (tradeImport.trade_swap == null)
|
|
{
|
|
throw new Exception($"该交易[{tradeImport.TradeNumber}]对应的trade_swap未赋值");
|
|
}
|
|
|
|
double costFee = 0;
|
|
if (isForGet)
|
|
{
|
|
var underlying = DataCacheProvider.GetUnderlyingDataSource().GetData(tradeImport.UnderlyingCode);
|
|
costFee += (tradeImport.trade_swap.GetSingleFee ?? 0) * (tradeCash.UnwindNotional ?? 0) / underlying.ContractSize;
|
|
costFee += (tradeImport.trade_swap.GetUnAnnualRate ?? 0) * (tradeCash.UnwindNotional ?? 0) * (isOpenFee ? (tradePosition.SpotPrice ?? 0) : (tradeCash.FinalPrice ?? 0));
|
|
}
|
|
else
|
|
{
|
|
var underlying = DataCacheProvider.GetUnderlyingDataSource().GetData(tradeImport.UnderlyingCode);
|
|
costFee += (tradeImport.trade_swap.PaySingleFee ?? 0) * (tradeCash.UnwindNotional ?? 0) / underlying.ContractSize;
|
|
costFee += (tradeImport.trade_swap.PayUnAnnualRate ?? 0) * (tradeCash.UnwindNotional ?? 0) * (isOpenFee ? (tradePosition.SpotPrice ?? 0) : (tradeCash.FinalPrice ?? 0));
|
|
}
|
|
|
|
return costFee.FormatValue(2);
|
|
}
|
|
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
public static double GetInitialAmountSwapGet(OtcTradeBase trade, trade_swap trade_swap, double lastFinalPrice
|
|
, double price, double stockEqvNotional, DateTime valueDate, DateTime? preSwapDate)
|
|
{
|
|
double? initialAmount;
|
|
if (trade_swap.IsGetFloatingProfit)
|
|
{
|
|
var annualRate = GetAnnualVarIncomeRate(trade_swap, valueDate, preSwapDate, trade.StartDate.Value, trade.ExerciseDate.Value);
|
|
initialAmount = GetInitialAmountSwap(lastFinalPrice, price
|
|
, (trade_swap.GetNotional ?? 0) * stockEqvNotional / trade.OriginalStockEqvNotional.Value
|
|
, trade_swap.GetLongShort, annualRate);
|
|
}
|
|
else
|
|
{
|
|
initialAmount = trade_swap.GetFixedProfit * stockEqvNotional / trade.OriginalStockEqvNotional;
|
|
}
|
|
|
|
return (initialAmount ?? 0).FormatValue(2);
|
|
}
|
|
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
public static double GetInitialAmountSwapPay(OtcTradeBase trade, trade_swap trade_swap, double lastFinalPrice
|
|
, double price, double stockEqvNotional, DateTime valueDate, DateTime? preSwapDate)
|
|
{
|
|
double? initialAmount;
|
|
|
|
if (trade_swap.IsPayFloatingProfit)
|
|
{
|
|
var annualRate = GetAnnualVarIncomeRate(trade_swap, valueDate, preSwapDate, trade.StartDate.Value, trade.ExerciseDate.Value);
|
|
initialAmount = GetInitialAmountSwap(lastFinalPrice, price
|
|
, (trade_swap.PayNotional ?? 0) * stockEqvNotional / trade.OriginalStockEqvNotional.Value
|
|
, trade_swap.PayLongShort, annualRate);
|
|
}
|
|
else
|
|
{
|
|
initialAmount = trade_swap.PayFixedProfit * stockEqvNotional / trade.OriginalStockEqvNotional;
|
|
}
|
|
|
|
return (initialAmount ?? 0).FormatValue(2);
|
|
}
|
|
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
/// <param name="lastFinalPrice"></param>
|
|
/// <param name="price"></param>
|
|
/// <param name="notional"></param>
|
|
/// <param name="longShort"></param>
|
|
/// <param name="annualVarIncomeRate">年化浮动收益率(为null表示非年化)</param>
|
|
/// <returns></returns>
|
|
public static double GetInitialAmountSwap(double lastFinalPrice, double price, double notional, string longShort, double? annualVarIncomeRate)
|
|
{
|
|
var amount = (price - lastFinalPrice) * notional.Normalize() * (longShort == "多头" ? 1 : -1);
|
|
|
|
return annualVarIncomeRate.HasValue ? amount * annualVarIncomeRate.Value : amount;
|
|
}
|
|
|
|
private static List<Date> GetSwapDatesBetween(string swapTimeAndRate, DateTime startDate, DateTime endDate)
|
|
{
|
|
var customizedResults = QdpHelper.ParseAutocallCustomizedInfo(swapTimeAndRate);
|
|
var dates = customizedResults.Item1;
|
|
if (dates == null)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
return dates.Where(x => x.DateTime > startDate && x.DateTime <= endDate).ToList();
|
|
}
|
|
|
|
public static double GetSwapRateByDate(string swapTimeAndRate, DateTime valueDate)
|
|
{
|
|
double swapRate = 0;
|
|
var customizedResults = QdpHelper.ParseAutocallCustomizedInfo(swapTimeAndRate);
|
|
var dates = customizedResults.Item1;
|
|
if (dates == null)
|
|
{
|
|
return swapRate;
|
|
}
|
|
|
|
var getSwapRates = customizedResults.Item2;
|
|
var latestDate = dates.Where(x => x.DateTime >= valueDate).OrderBy(x => x.DateTime).FirstOrDefault();
|
|
|
|
//展期情况互换利率获取最后一个日期的互换利率
|
|
if (latestDate == null)
|
|
{
|
|
latestDate = dates.Max();
|
|
}
|
|
|
|
if (getSwapRates != null && getSwapRates.Any())
|
|
{
|
|
swapRate = getSwapRates[GetDateIndex(dates, latestDate)];
|
|
}
|
|
|
|
return swapRate;
|
|
}
|
|
|
|
private static int GetDateIndex(Date[] source, Date value)
|
|
{
|
|
if (source is null)
|
|
{
|
|
throw new ArgumentNullException(nameof(source));
|
|
}
|
|
|
|
var index = 0;
|
|
foreach (var item in source)
|
|
{
|
|
if (item.DateTime == value.DateTime)
|
|
{
|
|
return index;
|
|
}
|
|
|
|
index++;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 获取互换交易变动收益年化率,返回null表示非年化
|
|
/// </summary>
|
|
/// <param name="trade_swap">互换交易</param>
|
|
/// <param name="valueDate">结算日期</param>
|
|
/// <param name="preSwapDate">上次互换日期</param>
|
|
/// <param name="tradeStartDate">交易开始日期</param>
|
|
/// <returns>返回null表示非年化</returns>
|
|
public static double? GetAnnualVarIncomeRate(trade_swap trade_swap, DateTime valueDate, DateTime? preSwapDate, DateTime tradeStartDate, DateTime exerciseDate)
|
|
{
|
|
if (trade_swap is null)
|
|
{
|
|
throw new ArgumentNullException(nameof(trade_swap));
|
|
}
|
|
|
|
//浮动收益是否年化
|
|
if (!trade_swap.AnnualVarIncome)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
if (!trade_swap.AnnualDays.HasValue || trade_swap.AnnualDays < 1)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
DateTime startDate;
|
|
|
|
if (preSwapDate != null)
|
|
{
|
|
startDate = preSwapDate.Value.AddDays(1);
|
|
}
|
|
else
|
|
{
|
|
//2021-05-11:浮动收益年化时,首日计息规则会影响到收益金额的计算
|
|
bool calcFirst = trade_swap.RateCalcMode.StartsWith("1");//算头
|
|
startDate = calcFirst ? tradeStartDate : tradeStartDate.AddDays(1);
|
|
}
|
|
|
|
var days = (valueDate - startDate.Date).Days + 1;
|
|
|
|
if (exerciseDate == valueDate && trade_swap.RateCalcMode.EndsWith("0"))
|
|
{
|
|
days -= 1;
|
|
}
|
|
|
|
if (days < 0)
|
|
{
|
|
days = 0;
|
|
}
|
|
|
|
return (double)days / trade_swap.AnnualDays.Value;
|
|
}
|
|
}
|
|
}
|