767 lines
30 KiB
C#
767 lines
30 KiB
C#
using Qdp.ComputeService.Data.CommonModels.MarketInfos;
|
|
using Qdp.ComputeService.Data.CommonModels.MarketInfos.CurveDefinitions;
|
|
using Qdp.Foundation.Implementations;
|
|
using Qdp.Pricing.Base.Enums;
|
|
using Qdp.Pricing.Base.Implementations;
|
|
using Qdp.Pricing.Base.Interfaces;
|
|
using Qdp.Pricing.Base.Utilities;
|
|
using Qdp.Pricing.Library.Options.Utilities;
|
|
using YLErp.Configuration;
|
|
|
|
namespace YLErp.QdpModule
|
|
{
|
|
/// <summary>
|
|
/// QDP类库帮助类
|
|
/// </summary>
|
|
public static class QdpHelper
|
|
{
|
|
/// <summary>
|
|
/// 默认到期日计算
|
|
/// </summary>
|
|
/// <param name="contractCode"></param>
|
|
/// <returns></returns>
|
|
public static DateTime defaultMaturityDateFromContract(string contractCode)
|
|
{
|
|
//上海规则
|
|
//合约交割月份的15日(遇法定假日顺延)
|
|
var shCodes = new string[] { "CU", "AL", "ZN", "PB", "NI", "AU", "AG", "RB", "WR", "HC", "BU", "RU" };
|
|
|
|
//合约交割月份前一月份的最后一个交易日,
|
|
var shPreMonthLastDay = new string[] { "FU", "SC" };
|
|
|
|
var shPreMonthFifthLastDay = new string[] { "IM" };
|
|
|
|
//大连规则
|
|
//合约月份第10个交易日
|
|
var dalianCodes = new string[] { "M", "Y", "A", "B", "P", "C", "CS", "JD", "BB", "FB", "L", "V", "PP", "J", "JM", "I" };
|
|
|
|
//special dalian rule, EGG
|
|
//合约交割月份前一月份的倒数第4个交易日
|
|
var dalianEggCodes = new string[] { "JD" };
|
|
|
|
//zhenzhou rule
|
|
//合约月份第10个交易日
|
|
var zhenzhouCodes = new string[] { "SR", "CF", "ZC", "FG", "TA", "MA", "WH", "PM", "SM", "RM", "RI", "LR", "JR", "RS", "OI", "SF", "CY", "AP" };
|
|
|
|
var calendar = CalendarImpl.Get("chn");
|
|
|
|
Date adjsuted;
|
|
if (strStartWith(contractCode, shCodes, out var estimate))
|
|
{
|
|
//SH rule: 15th of delivery date, modified following
|
|
var unAdjusted = new Date(estimate.Year, estimate.Month, 15);
|
|
adjsuted = calendar.Adjust(unAdjusted, BusinessDayConvention.ModifiedFollowing);
|
|
}
|
|
else if (strStartWith(contractCode, shPreMonthLastDay, out estimate))
|
|
{
|
|
//SH special rule, last biz day of previous month;
|
|
var unAdjusted = new Date(estimate.Year, estimate.Month, 1);
|
|
adjsuted = calendar.Adjust(unAdjusted, BusinessDayConvention.Previous);
|
|
}
|
|
else if (strStartWith(contractCode, shPreMonthFifthLastDay, out estimate))
|
|
{
|
|
//SH rule, 5th last biz day of previous month
|
|
var unAdjusted = new Date(estimate.Year, estimate.Month, 1);
|
|
for (var i = 1; i <= 5; i++)
|
|
{
|
|
unAdjusted = calendar.Adjust(unAdjusted.AddDays(-1), BusinessDayConvention.Previous);
|
|
}
|
|
adjsuted = unAdjusted;
|
|
}
|
|
else if (strStartWith(contractCode, dalianCodes, out estimate) || strStartWith(contractCode, zhenzhouCodes, out estimate))
|
|
{
|
|
//Dalian rule, 10th biz day of contract maturity month
|
|
var unAdjusted = new Date(estimate.Year, estimate.Month, 1);
|
|
for (var i = 1; i <= 10; i++)
|
|
{
|
|
unAdjusted = calendar.Adjust(unAdjusted.AddDays(1), BusinessDayConvention.ModifiedFollowing);
|
|
}
|
|
adjsuted = unAdjusted;
|
|
}
|
|
else if (strStartWith(contractCode, dalianEggCodes, out estimate))
|
|
{
|
|
//Dalian Egg rule, 4th last biz day of previous month
|
|
var unAdjusted = new Date(estimate.Year, estimate.Month, 1);
|
|
for (var i = 1; i <= 4; i++)
|
|
{
|
|
unAdjusted = calendar.Adjust(unAdjusted.AddDays(-1), BusinessDayConvention.Previous);
|
|
}
|
|
adjsuted = unAdjusted;
|
|
}
|
|
else
|
|
{
|
|
adjsuted = calendar.NextBizDay(new Term("1Y").Next(new Date(DateTime.Now)));
|
|
}
|
|
|
|
|
|
return adjsuted.DateTime;
|
|
}
|
|
private static bool strStartWith(string str, string[] InThisSet, out Date estimate)
|
|
{
|
|
var found = InThisSet.Where(x => str.StartsWith(x));
|
|
//default
|
|
estimate = new Date(DateTime.Today);
|
|
//locate delivery day from delivery code
|
|
//RB1803 => 20180301
|
|
|
|
if (found.Count() > 0)
|
|
{
|
|
try
|
|
{
|
|
var head = InThisSet.First(x => str.StartsWith(x));
|
|
var yearDates = "";
|
|
string[] zhenzhouCodes = { "SR", "CF", "ZC", "FGM", "TA", "MA", "WH", "PM" };
|
|
if (zhenzhouCodes.Contains(head)) // 郑州商品交易所的默认到期日做特殊处理
|
|
{
|
|
yearDates = "1" + str.Remove(0, head.Length);
|
|
}
|
|
else
|
|
{
|
|
yearDates = str.Remove(0, head.Length);
|
|
}
|
|
//var yearDates = str.Split(head)[1];
|
|
var year = Convert.ToInt32("20" + yearDates[0] + yearDates[1]);
|
|
try
|
|
{
|
|
var month = Convert.ToInt32(yearDates[2].ToString() + yearDates[3].ToString());
|
|
estimate = new Date(year, month, 1);
|
|
}
|
|
catch (Exception)
|
|
{
|
|
estimate = new Date(year, 1, 1);
|
|
}
|
|
}
|
|
catch (Exception)
|
|
{ }
|
|
}
|
|
|
|
return found.Count() > 0;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 根据term和valueDate计算到期日
|
|
/// </summary>
|
|
/// <param name="valueDate"></param>
|
|
/// <param name="term"></param>
|
|
/// <returns></returns>
|
|
public static DateTime getMaturityDate(DateTime valueDate, string term)
|
|
{
|
|
return new Term(term).Next(new Date(valueDate)).DateTime;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 从字符串中解析观察日列表
|
|
/// 如:2019-07-16,2019-08-16
|
|
/// </summary>
|
|
public static Date[] ParseObservationDate(string observationDateStr)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(observationDateStr))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
var fields = observationDateStr.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
|
|
|
|
var dateStrs = fields[0].Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
|
|
if (dateStrs == null || dateStrs.Length == 0)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
return dateStrs.Select(x => new Date(DateTime.Parse(x))).ToArray();
|
|
}
|
|
|
|
public static DateTime[] GetObservationDatesFromString(string str)
|
|
{
|
|
var dates = ParseObservationDate(str);
|
|
if (dates == null)
|
|
{
|
|
return null;
|
|
}
|
|
return dates.Select(x => x.DateTime).ToArray();
|
|
}
|
|
|
|
/// <summary>
|
|
/// 解析凤凰、雪球的自定义信息字符串,其格式为
|
|
/// {自定义敲出观察日序列};{敲出障碍价格序列};{票息序列}
|
|
/// 每个序列内部都以逗号分隔多个元素
|
|
/// </summary>
|
|
/// <param name="str"></param>
|
|
/// <returns>返回的元组,第一个元素为敲出观察日列表,第二个元素为敲出障碍价格列表,第三个元素为票息列表</returns>
|
|
public static Tuple<Date[], double[], double[]> ParseAutocallCustomizedInfo(string str)
|
|
{
|
|
Date[] dates = null;
|
|
double[] customizedKOBarriers = null;
|
|
double[] customizedCoupons = null;
|
|
|
|
if (!string.IsNullOrWhiteSpace(str))
|
|
{
|
|
//if (!str.Contains(";"))
|
|
//{
|
|
// str = ConvertObservationFromTableToString(str);
|
|
//}
|
|
|
|
var fields = str.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
|
|
|
|
if (fields != null && fields.Length > 0)
|
|
{
|
|
dates = ParseObservationDate(fields[0]);
|
|
}
|
|
|
|
if (fields != null && fields.Length > 1)
|
|
{
|
|
customizedKOBarriers = ParseDoubleNumbers(fields[1]);
|
|
}
|
|
|
|
if (fields != null && fields.Length > 2)
|
|
{
|
|
customizedCoupons = ParseDoubleNumbers(fields[2]);
|
|
}
|
|
}
|
|
|
|
return Tuple.Create(dates, customizedKOBarriers, customizedCoupons);
|
|
}
|
|
/// <summary>
|
|
/// 用于qdp 凤凰期权 看涨时,障碍价格为空,该障碍价格等于期初价格的100倍;看跌时,障碍价格为空,该障碍价格等于期初价格的百分之一
|
|
/// </summary>
|
|
public static Tuple<Date[], double[], double[]> ParseAutocallCustomizedInfoV2(string str, OptionType callput, double spotPrice, bool isMoneynessOption)
|
|
{
|
|
Date[] dates = null;
|
|
double[] customizedKOBarriers = null;
|
|
double[] customizedCoupons = null;
|
|
|
|
if (!string.IsNullOrWhiteSpace(str))
|
|
{
|
|
var fields = str.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
|
|
|
|
if (fields != null && fields.Length > 0)
|
|
{
|
|
dates = ParseObservationDate(fields[0]);
|
|
}
|
|
|
|
if (fields != null && fields.Length > 1)
|
|
{
|
|
customizedKOBarriers = ParseDoubleNumbersV2(fields[1], callput, spotPrice, isMoneynessOption);
|
|
}
|
|
|
|
if (fields != null && fields.Length > 2)
|
|
{
|
|
customizedCoupons = ParseDoubleNumbers(fields[2]);
|
|
}
|
|
}
|
|
|
|
return Tuple.Create(dates, customizedKOBarriers, customizedCoupons);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 不用于用于qdp 凤凰期权
|
|
/// {自定义敲出观察日序列};{敲出障碍价格序列}; 只有解析两列
|
|
/// </summary>
|
|
/// <param name="str"></param>
|
|
/// <returns>返回的元组,第一个元素为敲出观察日列表,第二个元素为敲出障碍价格列表</returns>
|
|
public static Tuple<Date[], double[]> ParseAutocallCustomizedInfoV3(string str)
|
|
{
|
|
Date[] dates = null;
|
|
double[] customizedKOBarriers = null;
|
|
|
|
if (!string.IsNullOrWhiteSpace(str))
|
|
{
|
|
//if (!str.Contains(";"))
|
|
//{
|
|
// str = ConvertObservationFromTableToString(str);
|
|
//}
|
|
|
|
var fields = str.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
|
|
|
|
if (fields != null && fields.Length > 0)
|
|
{
|
|
dates = ParseObservationDate(fields[0]);
|
|
}
|
|
|
|
if (fields != null && fields.Length > 1)
|
|
{
|
|
customizedKOBarriers = ParseDoubleNumbers(fields[1]);
|
|
}
|
|
}
|
|
|
|
return Tuple.Create(dates, customizedKOBarriers);
|
|
}
|
|
private static double[] ParseDoubleNumbers(string str)
|
|
{
|
|
var values = str.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
|
|
if (values == null || values.Length == 0)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
//try
|
|
//{
|
|
// return values.Select(x => double.Parse(x)).ToArray();
|
|
//}
|
|
//catch
|
|
//{
|
|
// return null;
|
|
//}
|
|
|
|
var dvalues = new double[values.Length];
|
|
for (var i = 0; i < values.Length; i++)
|
|
{
|
|
if (double.TryParse(values[i], out var d))
|
|
{
|
|
dvalues[i] = d;
|
|
}
|
|
else
|
|
{
|
|
return null;
|
|
}
|
|
}
|
|
return dvalues;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 凤凰期权 看涨时,障碍价格为空,该障碍价格等于期初价格的100倍;看跌时,障碍价格为空,该障碍价格等于期初价格的百分之一
|
|
/// </summary>
|
|
private static double[] ParseDoubleNumbersV2(string str, OptionType callput, double spotPrice, bool isMoneynessOption)
|
|
{
|
|
var values = str.Split(new char[] { ',' });
|
|
if (values == null || values.Length == 0)
|
|
{
|
|
return null;
|
|
}
|
|
double[] newVs = new double[values.Length];
|
|
int i = 0;
|
|
foreach (var v in values)
|
|
{
|
|
double newV = 0;
|
|
if (string.IsNullOrEmpty(v))
|
|
{
|
|
switch (callput)
|
|
{
|
|
case OptionType.Call:
|
|
newV = isMoneynessOption ? 100 : (spotPrice * 100);
|
|
break;
|
|
case OptionType.Put:
|
|
newV = isMoneynessOption ? 0.01 : (spotPrice / 100);
|
|
break;
|
|
default: break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
newV = double.Parse(v);
|
|
}
|
|
newVs[i] = newV;
|
|
i++;
|
|
}
|
|
return newVs;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 解析亚式期权的Fixing价格序列
|
|
/// </summary>
|
|
/// <param name="fixings"></param>
|
|
/// <returns></returns>
|
|
public static Dictionary<Date, double> ParseFixingsFromString(string fixings)
|
|
{
|
|
return string.IsNullOrWhiteSpace(fixings)
|
|
? new Dictionary<Date, double>() :
|
|
fixings.Split(QdpConsts.Semilicon)
|
|
.Select(x =>
|
|
{
|
|
var splits = x.Split(QdpConsts.Comma);
|
|
return Tuple.Create(splits[0].ToDate(), double.Parse(splits[1]));
|
|
}).ToDictionary(x => x.Item1, x => x.Item2);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 将期限字符串,转换成一组日期
|
|
/// </summary>
|
|
/// <param name="valueDate"></param>
|
|
/// <param name="expireTenors"></param>
|
|
/// <param name="volSurfaceKeyDateShift"></param>
|
|
/// <param name="excludeEndDate"></param>
|
|
/// <returns></returns>
|
|
public static List<Date> ConvertTenorsToDates(Date valueDate, string[] expireTenors, VolSurfaceKeyDateShiftEnum volSurfaceKeyDateShift, bool excludeEndDate)
|
|
{
|
|
var dates = new List<Date>();
|
|
var calendar = CalendarImpl.Get("chn");
|
|
//解析到期日
|
|
for (var i = 0; i < expireTenors.Length; ++i)
|
|
{
|
|
var expectedTermString = expireTenors[i];
|
|
if (Term.IsTerm(expectedTermString))
|
|
{
|
|
var keyPointDate = new Term(expectedTermString).Next(valueDate);
|
|
if (excludeEndDate)
|
|
{
|
|
keyPointDate = keyPointDate.AddDays(-1);
|
|
}
|
|
|
|
if (calendar.IsHoliday(keyPointDate))
|
|
{
|
|
switch (volSurfaceKeyDateShift)
|
|
{
|
|
case VolSurfaceKeyDateShiftEnum.NextBizDay:
|
|
keyPointDate = calendar.NextBizDay(keyPointDate);
|
|
break;
|
|
case VolSurfaceKeyDateShiftEnum.PrevBizDay:
|
|
keyPointDate = calendar.PrevBizDay(keyPointDate);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
dates.Add(keyPointDate);
|
|
}
|
|
}
|
|
return dates;
|
|
}
|
|
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
public static bool IsCall(string callPut)
|
|
{
|
|
if (string.IsNullOrEmpty(callPut))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return (OptionType)Enum.Parse(typeof(OptionType), callPut) == OptionType.Call;
|
|
}
|
|
|
|
public static double AnnualizeFactor(DateTime startDate, DateTime endDate, IDayCount dayCount)
|
|
{
|
|
return dayCount.CalcDayCountFraction(new Date(startDate), new Date(endDate));
|
|
}
|
|
|
|
public static double AnnualizeFactor(double ttmDays, IDayCount dayCount)
|
|
{
|
|
return ttmDays / dayCount.DaysInYear();
|
|
}
|
|
|
|
/// <summary>
|
|
/// 计算精确的TTM
|
|
/// </summary>
|
|
/// <param name="from">开始日期</param>
|
|
/// <param name="to">结束日期</param>
|
|
/// <param name="systemDate">当前系统交易日</param>
|
|
/// <param name="serverDateTime">当前服务器时间</param>
|
|
/// <param name="dayCount">使用的DayCount</param>
|
|
/// <param name="hasNightMarket">是否有夜盘</param>
|
|
/// <param name="precisionOfMinute">是否精确到分钟</param>
|
|
/// <returns></returns>
|
|
public static double CalculateTTMDays(DateTime from, DateTime to, DateTime systemDate, DateTime serverDateTime, IDayCount dayCount, bool hasNightMarket, bool precisionOfMinute)
|
|
{
|
|
var tempFrom = new Date(from);
|
|
var tempTo = new Date(to);
|
|
var days = dayCount.DaysInPeriod(tempFrom, tempTo);
|
|
|
|
//当交易日期不等于系统日期时,或者系统日期小于服务器日期时,默认加一天
|
|
double addDays = 1;
|
|
|
|
if (PS.Config.Is润和)
|
|
{
|
|
//东证润和
|
|
addDays = GetAddDaysWithPhysicalPrecisionOfMinute(serverDateTime);
|
|
}
|
|
else
|
|
{
|
|
//当交易日期等于系统日期时,算出当天剩余的小数天数,然后加上去
|
|
if (from.Date == systemDate && systemDate.Date >= serverDateTime.Date)
|
|
{
|
|
addDays = GetAddDays(serverDateTime, hasNightMarket, precisionOfMinute);
|
|
|
|
//如果系统日期大于服务器日期(自然日期),而时间大于收盘时间(15点)且小于夜盘开盘时间(21点)
|
|
//说明系统在夜盘开始前切换了交易日,此时应将TTM增加完整的一个交易日
|
|
if (systemDate.Date > serverDateTime.Date
|
|
&& serverDateTime.Hour >= 15 && serverDateTime.Hour < 21)
|
|
{
|
|
addDays += 1;
|
|
}
|
|
}
|
|
}
|
|
var ttmDays = days + addDays;
|
|
return ttmDays < 0 ? 0 : ttmDays;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 计算精确的TTM
|
|
/// </summary>
|
|
/// <param name="from">开始日期</param>
|
|
/// <param name="to">结束日期</param>
|
|
/// <param name="systemDate">当前系统交易日</param>
|
|
/// <param name="serverDateTime">当前服务器时间</param>
|
|
/// <param name="dayCount">使用的DayCount</param>
|
|
/// <param name="hasNightMarket">是否有夜盘</param>
|
|
/// <param name="precisionOfMinute">是否精确到分钟</param>
|
|
/// <returns></returns>
|
|
public static double CalculateTTMDaysForXiangYu(DateTime from, DateTime to, DateTime systemDate, DateTime serverDateTime, IDayCount dayCount, bool hasNightMarket, bool precisionOfMinute)
|
|
{
|
|
//最后一天平仓时,ttm按分钟算
|
|
//平仓日和系统交易日相等时,服务器时间的判断才有意义
|
|
if (from == to && from <= systemDate)
|
|
{
|
|
if (serverDateTime.TimeOfDay < new TimeSpan(10, 15, 0))//判断当前时间是否是十点十五分前
|
|
{
|
|
//计算从前一天21点到现在经过的时间;
|
|
double Secondsum = (serverDateTime.Hour + 3) * 60 + serverDateTime.Minute;
|
|
var value = (795 - Secondsum) / 795d;
|
|
return value;
|
|
}
|
|
else if (systemDate.Date > serverDateTime.Date && serverDateTime.Hour >= 15)//当系统交易日大于当前服务器时间且系统时间大于15点时,说明系统切过日期,且最后一个交易日的夜盘开始了.
|
|
{
|
|
//夜盘时段计算从21点到现在经过的分钟;
|
|
double Secondsum = serverDateTime.Hour < 21 ? 0 : ((serverDateTime.Hour - 21) * 60 + serverDateTime.Minute);
|
|
//795 = 13 * 60 + 15
|
|
var value = (795 - Secondsum) / 795d;
|
|
return value;
|
|
}
|
|
else//否则就是已经过期了;
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
return CalculateTTMDays(from, to, systemDate, serverDateTime, dayCount, hasNightMarket, precisionOfMinute);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 获取交易当天需要添加的小数天数
|
|
/// </summary>
|
|
/// <param name="variety">品种</param>
|
|
/// <param name="precisionOfMinute">是否要精确到分钟级别</param>
|
|
/// <returns></returns>
|
|
public static double GetAddDays(DateTime now, bool hasNightMarket, bool precisionOfMinute = false)
|
|
{
|
|
return precisionOfMinute
|
|
? GetAddDaysWithPrecisionOfMinute(now, hasNightMarket)
|
|
: GetAddDaysWithPrecisionOfHalfDay(now, hasNightMarket);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 获取交易当天需要添加的小数天数,精确到半天,夜盘精确到三分之一天
|
|
/// </summary>
|
|
/// <param name="variety">品种</param>
|
|
/// <returns></returns>
|
|
private static double GetAddDaysWithPrecisionOfHalfDay(DateTime now, bool hasNightMarket)
|
|
{
|
|
if (hasNightMarket)
|
|
{
|
|
if (now.Hour >= 21)
|
|
{
|
|
return 1;
|
|
}
|
|
else if (now.Hour >= 0 && now.Hour < 12)
|
|
{
|
|
return 0.6666;
|
|
}
|
|
else if (now.Hour >= 12 && now.Hour < 15)
|
|
{
|
|
return 0.3333;
|
|
}
|
|
else
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (now.Hour >= 21 || (now.Hour >= 0 && now.Hour < 12))
|
|
{
|
|
return 1;
|
|
}
|
|
else if (now.Hour >= 12 && now.Hour < 15)
|
|
{
|
|
return 0.5;
|
|
}
|
|
else
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 在日内计算精确到分钟级别的TTM调整
|
|
/// 假设所有夜盘交易时间都为2小时
|
|
/// 上午交易时间为2.5小时,下午为1.5小时
|
|
///
|
|
/// 单元测试: GetAddDaysWithPrecisionOfMinuteTest
|
|
/// </summary>
|
|
/// <param name="hasNightMarket"></param>
|
|
/// <returns></returns>
|
|
private static double GetAddDaysWithPrecisionOfMinute(DateTime now, bool hasNightMarket)
|
|
{
|
|
return AnalyticalOptionPricerUtil.timeToMaturityFractionOnMaturityDate(now.TimeOfDay, hasNightMarket, false);
|
|
//double past;
|
|
|
|
//var totalTradingMiniutes = hasNightMarket ? (2 + 2.5 + 1.5) * 60 : (2.5 + 1.5) * 60;
|
|
//if (hasNightMarket)
|
|
//{
|
|
// if (now.Hour >= 21 && now.Hour < 23)
|
|
// {
|
|
// past = (now.Hour - 21) * 60 + now.Minute;
|
|
// }
|
|
// else if (now.Hour >= 23)
|
|
// {
|
|
// past = 2 * 60;
|
|
// }
|
|
// else if (now.Hour < 9)
|
|
// {
|
|
// past = 2 * 60;
|
|
// }
|
|
// else if ((now.Hour >= 9 && now.Hour <= 10) || (now.Hour == 11 && now.Minute <= 30))
|
|
// {
|
|
// past = 2 * 60 + (now.Hour - 9) * 60 + now.Minute;
|
|
// }
|
|
// else if ((now.Hour > 11 || (now.Hour == 11 && now.Minute > 30)) && (now.Hour < 13 || (now.Hour == 13 && now.Minute < 30)))
|
|
// {
|
|
// past = (2 + 2.5) * 60;
|
|
// }
|
|
// else if (((now.Hour == 13 && now.Minute >= 30) || now.Hour > 13) && (now.Hour < 15))
|
|
// {
|
|
// past = (2 + 2.5) * 60 + (now.Hour - 13.5) * 60 + now.Minute;
|
|
// }
|
|
// else if (now.Hour >= 15 && now.Hour < 21)
|
|
// {
|
|
// past = (2 + 2.5 + 1.5) * 60;
|
|
// }
|
|
// else
|
|
// {
|
|
// //should never be here
|
|
// throw new Exception("精确到分钟级别的TTM计算错误");
|
|
// }
|
|
//}
|
|
//else
|
|
//{
|
|
// if ((now.Hour >= 9 && now.Hour <= 10) || (now.Hour == 11 && now.Minute <= 30))
|
|
// {
|
|
// past = (now.Hour - 9) * 60 + now.Minute;
|
|
// }
|
|
// else if ((now.Hour > 11 || (now.Hour == 11 && now.Minute > 30)) && (now.Hour < 13 || (now.Hour == 13 && now.Minute < 30)))
|
|
// {
|
|
// past = 2.5 * 60;
|
|
// }
|
|
// else if (((now.Hour == 13 && now.Minute >= 30) || now.Hour > 13) && (now.Hour < 15))
|
|
// {
|
|
// past = 2.5 * 60 + (now.Hour - 13.5) * 60 + now.Minute;
|
|
// }
|
|
// else if (now.Hour >= 15 && now.Hour < 21)
|
|
// {
|
|
// past = (2.5 + 1.5) * 60;
|
|
// }
|
|
// else if (now.Hour >= 21 || now.Hour < 9)
|
|
// {
|
|
// past = 0;
|
|
// }
|
|
// else
|
|
// {
|
|
// //should never be here
|
|
// throw new Exception("精确到分钟级别的TTM计算错误");
|
|
// }
|
|
//}
|
|
|
|
//if (hasNightMarket)
|
|
//{
|
|
// if (now.Hour >= 21 && now.Hour < 23)
|
|
// {
|
|
// past = (now.Hour - 21) * 60 + now.Minute;
|
|
// }
|
|
// else if (now.Hour >= 23)
|
|
// {
|
|
// past = 2 * 60;
|
|
// }
|
|
// else if (now.Hour < 9)
|
|
// {
|
|
// past = 2 * 60;
|
|
// }
|
|
// else if (now.Hour >= 9 && (now.Hour <= 11 && now.Minute <= 30))
|
|
// {
|
|
// past = 2 * 60 + (now.Hour - 9) * 60 + now.Minute;
|
|
// }
|
|
// else if ((now.Hour >= 11 || (now.Hour == 11 && now.Minute > 30)) && (now.Hour <= 13 || (now.Hour == 13 && now.Minute < 30)))
|
|
// {
|
|
// past = (2 + 2.5) * 60;
|
|
// }
|
|
// else if ((now.Hour >= 13 && now.Minute >= 30) && now.Hour < 15)
|
|
// {
|
|
// past = (2 + 2.5) * 60 + (now.Hour - 13.5) * 60 + now.Minute;
|
|
// }
|
|
// else if (now.Hour >= 15 && now.Hour < 21)
|
|
// {
|
|
// past = (2 + 2.5 + 1.5) * 60;
|
|
// }
|
|
// else
|
|
// {
|
|
// //should never be here
|
|
// throw new Exception("精确到分钟级别的TTM计算错误");
|
|
// }
|
|
//}
|
|
//else
|
|
//{
|
|
// if (now.Hour >= 9 && (now.Hour <= 11 && now.Minute <= 30))
|
|
// {
|
|
// past = (now.Hour - 9) * 60 + now.Minute;
|
|
// }
|
|
// else if ((now.Hour >= 11 || (now.Hour == 11 && now.Minute > 30)) && (now.Hour <= 13 || (now.Hour == 13 && now.Minute < 30)))
|
|
// {
|
|
// past = 2.5 * 60;
|
|
// }
|
|
// else if ((now.Hour >= 13 && now.Minute >= 30) && (now.Hour < 15))
|
|
// {
|
|
// past = 2.5 * 60 + (now.Hour - 13.5) * 60 + now.Minute;
|
|
// }
|
|
// else if (now.Hour >= 15 && now.Hour < 21)
|
|
// {
|
|
// past = (2.5 + 1.5) * 60;
|
|
// }
|
|
// else if (now.Hour >= 21 || now.Hour < 9)
|
|
// {
|
|
// past = 0;
|
|
// }
|
|
// else
|
|
// {
|
|
// //should never be here
|
|
// throw new Exception("精确到分钟级别的TTM计算错误");
|
|
// }
|
|
//}
|
|
|
|
//return (totalTradingMiniutes - past) / totalTradingMiniutes;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 当天剩余的分钟数 / 一天总的分钟数
|
|
/// </summary>
|
|
/// <param name="now"></param>
|
|
/// <returns></returns>
|
|
private static double GetAddDaysWithPhysicalPrecisionOfMinute(DateTime now)
|
|
{
|
|
double totalTradingMiniutes = 24 * 60;
|
|
double past = now.Hour * 60 + now.Minute;
|
|
//到期日按照3点来算,所以需要把剩余的9个小时扣除
|
|
double minusNineHour = 9 * 60;
|
|
|
|
return (totalTradingMiniutes - past - minusNineHour) / totalTradingMiniutes;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 创建一个无风险利率曲线
|
|
/// </summary>
|
|
public static InstrumentCurveDefinition CreateRiskFreeCurve(string curveName, double riskFreeRate, string curveDayCount = "Act365")
|
|
{
|
|
//创建一个Flat curve
|
|
var rateMktDataList = new List<RateMktData>
|
|
{
|
|
new RateMktData("1D", riskFreeRate, "Spot", "None", curveName),
|
|
new RateMktData("3Y", riskFreeRate, "Spot", "None", curveName)
|
|
};
|
|
if (string.IsNullOrWhiteSpace(curveDayCount))
|
|
{
|
|
curveDayCount = "Act365";
|
|
}
|
|
var curveConvention = new CurveConvention(Guid.NewGuid().ToString(), "CNY", "ModifiedFollowing", "chn", curveDayCount, "Continuous", "Linear");
|
|
return new InstrumentCurveDefinition(curveName, curveConvention, rateMktDataList.ToArray(), "SpotCurve");
|
|
}
|
|
}
|
|
}
|