Open3

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

P-HunterP-Hunter

あなたは優秀な外国為替クオンツトレーダーです。
UNIXタイムスタンプからできるだけ有効な特徴量をたくさん生み出すことがあなたのタスクです。
今度は重要な経済指標の発表日時に関連した特徴量をつくりましょう。
まずはタスク遂行にあたっての計画を立ててください。

P-HunterP-Hunter

Cursor Compose

経済指標カレンダー取得スクリプトの解説

🎯 このスクリプトの目的

このスクリプトは、米国労働統計局(Bureau of Labor Statistics, BLS)から経済指標の発表スケジュールを自動的に取得するためのものです。為替トレーディングにおいて、これらの経済指標の発表は市場に大きな影響を与えるため、とても重要な情報となります。

💡 なぜこの情報が必要なのか?

  1. 市場への影響

    • 雇用統計や消費者物価指数(CPI)などの経済指標は、為替レートに大きな影響を与えます
    • 指標の発表前後で、市場のボラティリティ(価格変動)が大きくなる傾向があります
  2. トレーディング戦略への活用

    • 重要な経済指標の発表前後では、リスク管理を慎重に行う必要があります
    • 指標発表をきっかけとしたトレーディング戦略を立てることもできます

🔍 スクリプトの主な機能

  1. データの取得

    • BLSのウェブサイトから、指定した年の経済指標発表スケジュールを取得します
    • 2000年から2024年までの期間をカバーしています
  2. データの整形

    • 取得したデータから以下の情報を抽出します:
      • 経済指標の名前
      • 発表日
      • 発表時刻(米国東部時間)
    • データはPandasのDataFrame形式で整理されます
  3. データの保存

    • 取得したデータはeconomic_release_calendar_us.parquetファイルとして保存されます
    • Parquet形式を使用することで、効率的なデータ保存と読み込みが可能です

📊 具体例

例えば、以下のような情報が取得できます:

  • 雇用統計(Non-farm Payrolls)の発表日時
  • 消費者物価指数(CPI)の発表日時
  • 生産者物価指数(PPI)の発表日時

🚀 活用方法

  1. 特徴量エンジニアリング

    • 経済指標の発表日までの残り日数を特徴量として使用
    • 発表前後の市場の動きを分析する際の基礎データとして活用
  2. リスク管理

    • 重要指標の発表前にポジションサイズを調整
    • 発表時の急激な価格変動に備えた戦略の構築
  3. バックテスト

    • 過去の経済指標発表が市場に与えた影響を分析
    • 効果的なトレーディング戦略の開発に活用

🔗 関連知識

  • 経済指標:経済の状態を示す統計データ
  • 為替市場:通貨の交換レートが決定される国際市場
  • ボラティリティ:価格の変動性を示す指標
  • クオンツトレーディング:数量的分析に基づく取引手法

このスクリプトは、データ駆動型の為替トレーディングにおける重要な基礎データを提供する役割を果たしています。

P-HunterP-Hunter
import pandas as pd
from bs4 import BeautifulSoup
import re
from datetime import datetime, timezone
import requests
import time
import pytz

def extract_from_pre(soup, year):
    """preタグ形式からデータを抽出"""
    pre_text = soup.find('pre').text.strip()
    lines = pre_text.split('\n')
    data_lines = [line for line in lines if line.strip() and not line.startswith(' ' * 10)]
    releases = []
    
    for line in data_lines[0:]:  # ヘッダー3行をスキップ
        line = line.replace('\t', '   ')
        # space2つ以上を | に変換
        for i in [4,3,2]:
            line = re.sub(r'\s{' + str(i) + ',}', '|', line)
            match = re.match(r'(.+?)\|(.+?)\|(.+?)$', line)
            if match:
                break
            else:
                print(f"Error parsing line(i={i}): {line}")
                continue
        if not match:
            continue

        name, date_str, time_str = match.groups()
        date_str = date_str.split(',')[0]
        date_str = date_str.replace('.', '')
        time_str = time_str.strip()
        date_time_str = f"{year} {date_str} {time_str}"
        #space2つ以上を1つに変換
        date_time_str = date_time_str.replace('  ', ' ')
        try:
            date_time = datetime.strptime(date_time_str, f'%Y %B %d %I:%M %p')
            est = pytz.timezone('US/Eastern')
            date_time = est.localize(date_time)
        except ValueError:
            try:
                date_time = datetime.strptime(date_time_str, f'%Y %b %d %I:%M %p')
                est = pytz.timezone('US/Eastern')
                date_time = est.localize(date_time)
            except ValueError:
                print(f"Error parsing date: {date_time_str}")
                print(f"line: {name}|{date_str}|{time_str}")
                continue
        except Exception as e:
            print(f"Error parsing date: {date_time_str} - {e}")
            print(f"line: {name}|{date_str}|{time_str}")
            continue        
        releases.append({
            'name': name.strip(),
            'date': date_time.date(),
            'datetime': date_time,
            'year': year
        })

    return releases

def extract_from_table(soup, year):
    """tableタグ形式からデータを抽出"""
    releases = []
    tables = soup.find_all('table', class_='release-list')
    est = pytz.timezone('US/Eastern')
    
    for table in tables:
        rows = table.find_all('tr')[1:]  # ヘッダー行をスキップ
        for row in rows:
            cells = row.find_all(['td'])
            if len(cells) >= 3:
                date_cell = cells[0].text.strip()
                time_cell = cells[1].text.strip()
                name_cell = cells[2].text.strip()
                
                if not time_cell or time_cell == '\xa0':  # 時間が空の場合(祝日など)
                    continue
                
                # 日付文字列から年を削除(yearパラメータを使用)
                date_str = re.sub(r',\s*\d{4}', f', {year}', date_cell)
                
                try:
                    date_time = datetime.strptime(f"{date_str} {time_cell}", '%A, %B %d, %Y %I:%M %p')
                    date_time = est.localize(date_time)
                    releases.append({
                        'name': name_cell,
                        'date': date_time.date(),
                        'datetime': date_time,
                        'year': year
                    })
                except ValueError as e:
                    print(f"Error parsing date: {date_str} {time_cell} - {e}")
                    continue
    
    return releases

def extract_release_calendar(year):
    """
    BLSのリリーススケジュールをスクレイピングしてデータフレームを返す
    
    Args:
        year (int): 取得したい年(例:2001)
    
    Returns:
        pd.DataFrame: リリーススケジュールのデータフレーム
    """
    url = f"https://www.bls.gov/schedule/{year}/home.htm"
    
    # Enhanced headers to better mimic a browser
    headers = {
        'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36',
        'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8',
        'Accept-Language': 'en-US,en;q=0.9',
        'Accept-Encoding': 'gzip, deflate, br',
        'Cache-Control': 'max-age=0',
        'Sec-Ch-Ua': '"Chromium";v="122", "Not(A:Brand";v="24", "Google Chrome";v="122"',
        'Sec-Ch-Ua-Mobile': '?0',
        'Sec-Ch-Ua-Platform': '"macOS"',
        'Sec-Fetch-Dest': 'document',
        'Sec-Fetch-Mode': 'navigate',
        'Sec-Fetch-Site': 'none',
        'Sec-Fetch-User': '?1',
        'Upgrade-Insecure-Requests': '1',
        'Referer': 'https://www.bls.gov/',
    }
    
    try:
        # Add a small delay before making the request
        time.sleep(2)
        
        session = requests.Session()
        response = session.get(url, headers=headers)
        response.raise_for_status()
        soup = BeautifulSoup(response.text, 'html.parser')
        
        # フォーマットを判定して適切な抽出方法を選択
        releases = []
        if soup.find('pre'):
            releases = extract_from_pre(soup, year)
        elif soup.find('table', class_='release-list'):
            releases = extract_from_table(soup, year)
        else:
            raise ValueError("Unsupported format: Neither pre tag nor release-list table found")
        
        if not releases:
            raise ValueError("No releases found")
        
        return pd.DataFrame(releases)
    
    except requests.exceptions.RequestException as e:
        print(f"Error fetching data for year {year}: {e}")
        return None
    except Exception as e:
        print(f"Error processing data for year {year}: {e}")
        return None

if __name__ == "__main__":
    df = pd.DataFrame()
    for year in range(2000, 2025):
        print(f"Extracting data for year {year}")
        df_year = extract_release_calendar(year)
        if df_year is not None:
            df = pd.concat([df, df_year], ignore_index=True)
    df.to_parquet('economic_release_calendar_us.parquet')


import pandas as pd
df = pd.read_parquet("economic_release_calendar_us.parquet")
df.tail()
name	date	datetime	year
3421	Employer Costs for Employee Compensation for S...	2024-12-17	2024-12-17 10:00:00-05:00	2024
3422	State Job Openings and Labor Turnover for Octo...	2024-12-17	2024-12-17 10:00:00-05:00	2024
3423	Work Experience of the Population (Annual) for...	2024-12-18	2024-12-18 10:00:00-05:00	2024
3424	Census of Fatal Occupational Injuries for Annu...	2024-12-19	2024-12-19 10:00:00-05:00	2024
3425	State Employment and Unemployment (Monthly) fo...	2024-12-20	2024-12-20 10:00:00-05:00	2024