自家製クオンツ

ソフトウェア技術を使って金融や投資の考察をしています。

MagicalNuts.Primitive.Calendar

f:id:yooce:20211128163314p:plain

ひとりアドベントカレンダー8日目です。今日は、MagicalNuts.Primitive.Calendarを解説します。

/// <summary>
/// カレンダーを表します。
/// </summary>
public class Calendar
{
    /// <summary>
    /// 非同期で準備します。
    /// </summary>
    /// <returns>成功したかどうか</returns>
    public virtual async Task<bool> SetUpAsync()
    {
        return true;
    }

    /// <summary>
    /// 休祝日かどうか判定します。
    /// </summary>
    /// <param name="dt">日時</param>
    /// <returns>休祝日かどうか</returns>
    public virtual bool IsHoliday(DateTime dt)
    {
        return (dt.DayOfWeek == DayOfWeek.Saturday || dt.DayOfWeek == DayOfWeek.Sunday);
    }

    /// <summary>
    /// days日前の営業日を取得します。
    /// </summary>
    /// <param name="dt">基準日</param>
    /// <param name="days">日数</param>
    /// <returns>days日前の営業日</returns>
    public DateTime GetBusinessDayBefore(DateTime dt, int days)
    {
        for (int i = 0; i < days; i++)
        {
            dt = GetFirstBusinessDayToBefore(dt.AddDays(-1));
        }
        return dt;
    }

    /// <summary>
    /// days日後の営業日を取得します。
    /// </summary>
    /// <param name="dt">基準日</param>
    /// <param name="days">日数</param>
    /// <returns>days日後の営業日</returns>
    public DateTime GetBusinessDayAfter(DateTime dt, int days)
    {
        for (int i = 0; i < days; i++)
        {
            dt = GetFirstBusinessDayToAfter(dt.AddDays(1));
        }
        return dt;
    }

    /// <summary>
    /// dt以前(dt含む)で最初の営業日を取得します。
    /// </summary>
    /// <param name="dt">基準日</param>
    /// <returns>dt以前(dt含む)で最初の営業日</returns>
    public DateTime GetFirstBusinessDayToBefore(DateTime dt)
    {
        while (IsHoliday(dt))
        {
            dt = dt.AddDays(-1);
        }
        return dt;
    }

    /// <summary>
    /// dt以後(dt含む)で最初の営業日を取得します。
    /// </summary>
    /// <param name="dt">基準日</param>
    /// <returns>dt以後(dt含む)で最初の営業日</returns>
    public DateTime GetFirstBusinessDayToAfter(DateTime dt)
    {
        while (IsHoliday(dt))
        {
            dt = dt.AddDays(1);
        }
        return dt;
    }
}

Calendarは市場の営業日、非営業日を判定する基底クラスです。実際に判定するメソッドはIsHoliday()ですが、このクラスでは週末の判定だけしかしないので、これを継承したクラスでIsHoliday()overrideする必要があります。ShareholderIncentiveSampleではSampleCalendarを実装し、週末に加えて祝日まで判定しています。

public class SampleCalendar : Calendar
{
    private readonly string[] datestrs =
    {
        "2020-01-01", "2020-01-02", "2020-01-03", "2020-01-13", "2020-02-11", "2020-02-24", "2020-03-20","2020-04-29", "2020-05-04", "2020-05-05",
        "2020-05-06", "2020-07-23", "2020-07-24", "2020-08-10", "2020-09-21", "2020-09-22", "2020-11-03", "2020-11-23", "2020-12-31"
    };

    private List<DateTime> Holidays = null;

    public override async Task<bool> SetUpAsync()
    {
        Holidays = new List<DateTime>();
        foreach (string datestr in datestrs)
        {
            Holidays.Add(DateTime.Parse(datestr));
        }
        return true;
    }

    public override bool IsHoliday(DateTime dt)
    {
        return dt.DayOfWeek == DayOfWeek.Saturday || dt.DayOfWeek == DayOfWeek.Sunday
            || Holidays.Where(holiday => holiday.Date == dt).Count() > 0;
    }
}

SampleCalendarでは、SetUpAsync()であらかじめ用意していた祝日を表す文字列の配列からDateTimeのリストを作り、overrideしたIsHoliday()でそれらを使った休祝日判定を行っています。尚、このクラスでは、2020年の祝日しか判定していませんのでご注意ください。

MagicalNutsではMagicalNuts.ShareholderIncentiveパッケージがCalendarの継承クラスを要求しますし、MagicalNuts.BackTestパッケージでも売買戦略によっては必要になってくると思います。