Open2

外国為替MLBot稼働にむけてAIが特徴量を生成するよ#4

P-HunterP-Hunter

Claude 3.5 sonnet

from datetime import datetime, timezone, timedelta
import calendar
import jpholiday
import holidays

class GotoDateDetector:
    """
    ゴトー日(5の倍数の日)の判定を行うクラス
    - 平日(月〜金)であること
    - 日本および米国の祝日でないこと
    - 翌営業日までの間にゴトー日がある場合は前倒しで当日をゴトー日とする
    - 月内の最終営業日は強制的にゴトー日となる
    を考慮して判定を行う
    """

    def __init__(self):
        # 日本のタイムゾーン
        self.jst = timezone(timedelta(hours=9))
        # ゴトー日のリスト
        self.goto_days = {5, 10, 15, 20, 25, 30}
        # 米国の祝日カレンダー
        self.us_holidays = holidays.US()

    def get_date_components(self, timestamp: int) -> tuple:
        """
        UNIXタイムスタンプから日付の要素を取得

        Args:
            timestamp (int): UNIXタイムスタンプ

        Returns:
            tuple: (datetime object, year, month, day, weekday)
        """
        try:
            date = datetime.fromtimestamp(timestamp, tz=self.jst)
            return (
                date,
                date.year,
                date.month,
                date.day,
                date.weekday()
            )
        except (ValueError, TypeError, OSError) as e:
            raise ValueError(f"Invalid timestamp: {timestamp}") from e

    def is_gotobi(self, day: int) -> bool:
        """
        指定された日がゴトー日(5の倍数の日)かどうかを判定

        Args:
            day (int): 日

        Returns:
            bool: ゴトー日の場合True
        """
        return day in self.goto_days

    def is_weekday(self, weekday: int) -> bool:
        """
        平日(月〜金)かどうかを判定

        Args:
            weekday (int): 曜日(0=月曜日, 6=日曜日)

        Returns:
            bool: 平日の場合True
        """
        return 0 <= weekday <= 4

    def is_us_holiday(self, date: datetime) -> bool:
        """
        米国の祝日かどうかを判定

        Args:
            date (datetime): 判定対象の日付

        Returns:
            bool: 米国の祝日の場合True
        """
        return date.date() in self.us_holidays

    def is_business_day(self, date: datetime) -> bool:
        """
        営業日(平日かつ日本・米国の祝日でない日)かどうかを判定

        Args:
            date (datetime): 判定対象の日付

        Returns:
            bool: 営業日の場合True
        """
        return (self.is_weekday(date.weekday()) and 
                not jpholiday.is_holiday(date.date()) and
                not self.is_us_holiday(date))

    def get_next_business_day(self, date: datetime) -> datetime:
        """
        次の営業日を取得

        Args:
            date (datetime): 基準日

        Returns:
            datetime: 次の営業日
        """
        next_date = date + timedelta(days=1)
        while not self.is_business_day(next_date):
            next_date += timedelta(days=1)
        return next_date

    def has_gotobi_before_next_business_day(self, date: datetime) -> bool:
        """
        次の営業日までの間にゴトー日があるかどうかを判定

        Args:
            date (datetime): 基準日

        Returns:
            bool: ゴトー日がある場合True
        """
        current = date + timedelta(days=1)
        next_business_day = self.get_next_business_day(date)
        
        while current < next_business_day:
            if self.is_gotobi(current.day):
                return True
            current += timedelta(days=1)
        return False

    def is_last_business_day_of_month(self, date: datetime) -> bool:
        """
        月内の最終営業日かどうかを判定

        Args:
            date (datetime): 判定対象の日付

        Returns:
            bool: 月内の最終営業日の場合True
        """
        # 翌日を取得
        next_date = date + timedelta(days=1)
        
        # 翌日が翌月の場合、当日が月末
        if next_date.month != date.month:
            return self.is_business_day(date)
        
        # 当日から月末まで、当日以外に営業日がない場合は最終営業日
        current = next_date
        last_day = date.replace(day=calendar.monthrange(date.year, date.month)[1])
        
        while current <= last_day:
            if self.is_business_day(current):
                return False
            current += timedelta(days=1)
            
        return self.is_business_day(date)

    def is_valid_gotobi(self, timestamp: int) -> bool:
        """
        指定された日時が有効なゴトー日かどうかを判定
        - ゴトー日である、または
        - 翌営業日までの間にゴトー日があり、当日が営業日である、または
        - 月内の最終営業日である
        のいずれかの条件を満たす必要がある

        Args:
            timestamp (int): 判定対象のUNIXタイムスタンプ

        Returns:
            bool: 有効なゴトー日の場合True

        Raises:
            ValueError: タイムスタンプが無効な場合
        """
        try:
            date_obj, _, _, day, _ = self.get_date_components(timestamp)

            # 当日が営業日でない場合は False
            if not self.is_business_day(date_obj):
                return False

            # 月内の最終営業日の場合は True
            if self.is_last_business_day_of_month(date_obj):
                return True

            # 当日がゴトー日の場合は True
            if self.is_gotobi(day):
                return True

            # 翌営業日までの間にゴトー日がある場合は True
            if self.has_gotobi_before_next_business_day(date_obj):
                return True

            return False

        except ValueError as e:
            raise ValueError(f"Error processing timestamp: {e}")

def is_gotobi_weekday_non_holiday(timestamp: int) -> bool:
    """
    指定されたUNIXタイムスタンプの日付が有効なゴトー日かどうかを判定する
    ユーティリティ関数

    Args:
        timestamp (int): 判定対象のUNIXタイムスタンプ

    Returns:
        bool: 有効なゴトー日の場合True

    Raises:
        ValueError: タイムスタンプが無効な場合
    """
    detector = GotoDateDetector()
    return detector.is_valid_gotobi(timestamp)