Strasmore Research
கற்றல் Matt Connorஆசிரியர் Matt Connor · data as of August 16, 2026 · refreshed weekly

h5i-db: Backtest-ல் Point-in-Time தரவு பயன்பாடு

Backtest-ல் look-ahead bias-ஐத் தவிர்க்க h5i-db எவ்வாறு உதவுகிறது? ஒவ்வொரு பதிவையும் வரிசைப்படுத்தி, filing lag தரவு மூலம் துல்லியமான வரலாற்றுத் தரவை இது வழங்குகிறது.

Point-in-time தரவு என்பது ஒரு குறிப்பிட்ட கடந்த கால தேதியில் தரவுத்தொகுப்பில் (dataset) இருந்த தகவல்களின் பதிவாகும். ஒரு backtest-ல், இது நீங்கள் நியாயப்படுத்தக்கூடிய முடிவுகளையும், எதிர்காலத் தரவுகளை முன்கூட்டியே பயன்படுத்தும் முடிவுகளையும் தனித்தனியாகப் பிரிக்க உதவுகிறது. h5i-db என்பது Rust மொழியில் எழுதப்பட்டு, Python API-யைக் கொண்ட ஒரு புதிய open-source time-series database ஆகும். இது ஒவ்வொரு பதிவையும் (write) ஒரு வரிசை எண்ணுடன் சேமித்து, எந்தவொரு வாசிப்புக்கும் (read) முந்தைய பதிப்பைத் தேர்வு செய்ய (pin) அனுமதிக்கிறது. கீழே, உண்மையான தாக்கல் செய்யப்பட்ட தரவுகளில் (filing data) அளவிடப்பட்ட தரவு கசிவு (leakage) மற்றும் உங்கள் மடிக்கணினியில் நீங்கள் இயக்கிப் பார்க்கக்கூடிய ஒரு சூழல் கொடுக்கப்பட்டுள்ளது.

Backtest-ல் point-in-time தரவு என்றால் என்ன?

ஒவ்வொரு சந்தை நிகழ்வும் இரண்டு நேர முத்திரைகளைக் (timestamps) கொண்டிருக்கும். நிகழ்வு நேரம் (Event time) என்பது ஒரு விஷயம் நடந்த நேரத்தைக் குறிக்கும். வருகை நேரம் (Arrival time) என்பது அந்தத் தகவல் நிறுவனத்திற்கு வெளியே உள்ளவர்களுக்குத் தெரிந்த நேரத்தைக் குறிக்கும். ஒரு காலாண்டு இருப்பு அறிக்கை, ஒரு காலாண்டின் கடைசி நாளில் இருந்த பங்குகளை விவரிக்கிறது, ஆனால் அது பொதுமக்களுக்கு பல வாரங்களுக்குப் பிறகுதான் கிடைக்கிறது. எனவே, நிகழ்வு நேரத்தை மட்டும் அடிப்படையாகக் கொண்டு ஒரு மாதிரியை (model) உருவாக்கினால், அந்த நேரத்தில் யாருக்கும் தெரியாத தகவல்களை அது பயன்படுத்தும் பிழை ஏற்படும்.

இந்த கால இடைவெளியை அளவிட முடியும். நிறுவன முதலீட்டாளர்கள் ஒவ்வொரு காலாண்டின் முடிவிலும் Form 13F-ஐ தாக்கல் செய்கிறார்கள். கீழே உள்ள அட்டவணை, ஒரு காலாண்டின் முடிவிற்கும் அந்த அறிக்கை தாக்கல் செய்யப்பட்ட நாளுக்கும் இடைப்பட்ட நாட்களை அளவிடுகிறது.

வினவவும்13F ஆவணங்கள் பொதுமக்களின் பார்வைக்கு வர எடுக்கும் காலம்
ஒவ்வொரு எண்ணின் பின்னணியில் உள்ள துல்லியமான SQL
WITH per_filing AS
(
    SELECT
        accession_number,
        any(toDate(parseDateTimeBestEffortOrNull(toString(period)))) AS period_end,
        any(toDate(filing_date))                                     AS filed_on
    FROM global_markets.stocks_13f_filings
    WHERE filing_date >= '2023-01-01'
    GROUP BY accession_number
)
SELECT
    toString(period_end)                                 AS period_end_date,
    countDistinct(accession_number)                      AS filings_count,
    round(avg(dateDiff('day', period_end, filed_on)), 1) AS avg_days_to_public
FROM per_filing
WHERE period_end IS NOT NULL
  AND filed_on >= period_end
  AND filed_on <= period_end + 400
GROUP BY period_end
HAVING filings_count >= 100
ORDER BY period_end
Run this yourself

2026-06-30-ல் முடிவடைந்த காலாண்டிற்கு, 10688 அறிக்கைகள் அவை உள்ளடக்கிய காலத்திற்குப் பிறகு சராசரியாக 34.1 நாட்களில் வந்து சேர்ந்தன. இந்த அட்டவணை 16 காலாண்டுகளுக்கு இந்த அளவீட்டைத் தொடர்கிறது. ஒரு மேலாளர் அறிக்கையைத் தாக்கல் செய்த நீண்ட காலத்திற்குப் பிறகும் அதில் திருத்தங்களைச் செய்யலாம். எனவே, கடந்த காலத்தைக் குறிக்கும் தரவு, அந்தத் தேதி கடந்த பிறகும் தொடர்ந்து மாறக்கூடும்.

Look-ahead bias ஒரு தரவு சேமிப்புச் சிக்கலாகும்

backtesting-ல் look-ahead bias குறித்த எமது வழிகாட்டி, தரவு கசிவை (leakage) ஒரு ஒழுக்கமான நடைமுறையாகக் கையாள்கிறது: ஒவ்வொரு feature-க்கும் காலதாமதத்தை (lag) ஏற்படுத்தி, வெளியீட்டுத் தேதிகளைக் கவனத்தில் கொள்ள வேண்டும். இந்த ஒழுக்கம் கடைப்பிடிக்கப்படும் வரை சிக்கல் இல்லை, ஆனால் யாராவது தவறு செய்யும்போது அதன் பாதிப்பு அமைதியாகவே இருக்கும். தரவு கசிந்த ஒரு backtest, மிகச்சிறந்த Sharpe ratio-வைக் காட்டும், ஆனால் எந்தப் பிழையையும் சுட்டிக்காட்டாது.

Point-in-time சேமிப்பு முறை, இந்த உத்தரவாதத்தை அடுத்த நிலைக்குக் கொண்டு செல்கிறது. ஒரு strategy-க்கு வழங்கப்படும் தரவு, ஒரு குறிப்பிட்ட பதிப்புடன் (version) இணைக்கப்பட்ட வாசிப்பிலிருந்து (read) பெறப்படும்போது, அந்தப் பதிப்பிற்குப் பிறகு எழுதப்பட்ட எந்தவொரு வரிசையும் (row) அதில் இடம்பெறாது; strategy குறியீடு என்ன செய்தாலும் இது மாறாது. இதனால், சரிபார்ப்பு என்பது குறியீட்டு ஆய்வாக (code review) இல்லாமல், வாசிப்பின் ஒரு பண்பாக மாறிவிடுகிறது.

Dividend-கள் இந்த கால இடைவெளியை மறுபக்கத்தில் காட்டுகின்றன. ஒரு ரொக்க dividend முதலில் அறிவிக்கப்பட்டு, பின்னர் ex-date-ஐ அடைகிறது. இன்று ஏற்றப்படும் ஒரு அட்டவணையில், ஒவ்வொரு பணப்பரிமாற்றத்திற்கும் இந்த இரண்டு தேதிகளும் இருக்கும். இதில், உருவகப்படுத்தப்படும் (simulated) தேதியில் இன்னும் அறிவிக்கப்படாத dividend-களும் அடங்கும்.

வினவவும்ஈவுத்தொகை அறிவிப்புக்கும் அதன் ex-date-க்கும் இடைப்பட்ட நாட்கள் (மாத வாரியாக)
ஒவ்வொரு எண்ணின் பின்னணியில் உள்ள துல்லியமான SQL
SELECT
    toString(toStartOfMonth(ex_dividend_date))                         AS month,
    round(avg(dateDiff('day', declaration_date, ex_dividend_date)), 1) AS avg_days_announced_ahead,
    countDistinct(ticker)                                              AS payers_count
FROM global_markets.stocks_dividends
WHERE ex_dividend_date >= toStartOfMonth(today() - 730)
  AND ex_dividend_date <  toStartOfMonth(today())
  AND declaration_date >= '1990-01-01'
  AND declaration_date <= ex_dividend_date
GROUP BY month
ORDER BY month
Run this yourself

2026-07-01-ல் தொடங்கிய மாதத்தில், அறிவிப்புகள் ex-dividend தேதிக்கு சராசரியாக 88.2 நாட்களுக்கு முன்பே வந்து சேர்ந்தன. இந்த ஆய்வு 24 மாத கால அளவை உள்ளடக்கியது. அந்த இடைவெளிக்குள் ஒரு உருவகப்படுத்தப்பட்ட தேதியில் தற்போதைய dividend அட்டவணையை வாசித்தால், அறிவிப்பு வருவதற்கு பல வாரங்களுக்கு முன்பே அந்தப் பணப்பரிமாற்றம் அங்கு பதிவாகியிருக்கும்.

சேமிக்கப்பட்ட தரவு வரலாறு மாற்றியமைக்கப்படுதல்

தாமதமாக வருவது ஒரு தோல்வி நிலை என்றால், மறுசீரமைப்பு (restatement) மற்றொரு தோல்வி நிலை ஆகும். நிறுவன நடவடிக்கைகளால் ஏற்கனவே பதிவான விலைகள் மாற்றியமைக்கப்படுகின்றன: நான்கு-க்கு-ஒன்று (4-for-1) பங்குப் பிரிப்புக்குப் பிறகு, சரிசெய்யப்பட்ட தொடரில் உள்ள முந்தைய அனைத்து விலைகளும் நான்கால் வகுக்கப்படுகின்றன. இதனால், இன்று தரவிறக்கம் செய்யப்படும் தரவு, வர்த்தகர் அன்று பார்த்த சந்தை நிலவரத்துடன் ஒத்துப்போகாது. பங்குப் பிரிப்புக்கு சரிசெய்யப்பட்ட விலை வரலாறு குறித்த எங்கள் குறிப்பு, இதற்கான கணக்கீடுகளை விளக்குகிறது. இதில் கவனிக்க வேண்டியது அதன் நிகழ்வெண் (frequency) ஆகும்.

வினவவும்ஒவ்வொரு காலாண்டிலும் நடைமுறைக்கு வரும் பங்குப் பிரிப்பு (forward மற்றும் reverse)
ஒவ்வொரு எண்ணின் பின்னணியில் உள்ள துல்லியமான SQL
SELECT
    toString(toStartOfQuarter(execution_date))    AS quarter_start_date,
    countDistinctIf(id, split_to > split_from)    AS forward_splits,
    countDistinctIf(id, split_to < split_from)    AS reverse_splits
FROM global_markets.stocks_splits
WHERE execution_date >= toStartOfQuarter(today() - 1460)
  AND execution_date <  toStartOfQuarter(today())
  AND split_from > 0
  AND split_to   > 0
GROUP BY quarter_start_date
ORDER BY quarter_start_date
Run this yourself

2026-04-01-ல் தொடங்கிய காலாண்டில், 131 முன்னோக்கிய பங்குப் பிரிப்புகளும் (forward splits) மற்றும் 303 தலைகீழ் பங்குப் பிரிப்புகளும் (reverse splits) நடைமுறைக்கு வந்தன. ஒவ்வொன்றும், ஒரு ஆராய்ச்சித் தரவுத் தொகுப்பில் ஏற்கனவே சேமிக்கப்பட்டிருக்கக்கூடிய விலை வரலாற்றை மறுசீரமைக்கின்றன. பதிப்பு மேலாண்மை கொண்ட சேமிப்பு (versioned storage) இந்த மறுசீரமைப்பைத் தடுக்காது. இது புதிய நிலையை ஒரு புதிய பதிப்பாகப் பதிவு செய்து, பழைய பதிப்பையும் வாசிக்கக்கூடிய நிலையில் வைத்திருக்கும். இதுவே காலாவதியான முடிவை, மீண்டும் உருவாக்கக்கூடிய (reproducible) முடிவாக மாற்றுகிறது.

செய்தி நேர முத்திரைகளும் (news timestamps) இதே போன்ற சிக்கலைச் சிறிய அளவில் கொண்டுள்ளன.

வினவவும்சந்தைச் செய்திகள் வெளியாகும் நேரம் (நியூயார்க் நேரப்படி)
ஒவ்வொரு எண்ணின் பின்னணியில் உள்ள துல்லியமான SQL
SELECT
    formatDateTime(toTimeZone(published_utc, 'America/New_York'), '%H:00') AS et_hour,
    countDistinct(id)                                                     AS articles
FROM global_markets.stocks_news
WHERE published_utc >= today() - 90
GROUP BY et_hour
ORDER BY et_hour
Run this yourself

நியூயார்க் சந்தையின் 24 மணிநேரங்களிலும் தலைப்புச் செய்திகள் தொடர்ந்து வெளியாகின்றன. கடந்த தொண்ணூறு நாட்களில், காலை 09:00 மணி நேரத்தில் 790 கட்டுரைகளும், இரவு 20:00 மணி நேரத்தில் 404 கட்டுரைகளும் வெளியாகியுள்ளன. ஒரு மாலை நேரச் செய்தியை, அன்றைய மாலை 4:00 மணி நேரச் சந்தை முடிவுடன் (close) இணைப்பது, சந்தை முடிவு நேரத்தை வர்த்தகம் செய்யும் ஒரு உத்திக்கு, பல மணிநேர முன்கூட்டியே தெரிந்த தகவலை (hindsight) வழங்குகிறது.

நீங்கள் இயக்கக்கூடிய ஒரு குறிப்பிட்ட காலக்கட்ட சூழல்

இங்குள்ள அனைத்தும் Python தொகுப்பில் இருக்கும். இந்தத் திட்டத்தில் Rust கட்டளை வரி கருவியும் (command line tool) உள்ளது, ஆனால் இந்த விளக்கத்திற்கு அது தேவையில்லை. மாதிரி தரவுகள் உள்ளூரிலேயே உருவாக்கப்படுகின்றன, எதையும் பதிவிறக்கம் செய்ய வேண்டியதில்லை.

  1. ஆகஸ்ட் 4, 2026 அன்று வெளியிடப்பட்ட pip install 'h5i-db==0.1.6'-ன் குறிப்பிட்ட பதிப்பை நிறுவவும். இதற்கு Python 3.9 அல்லது அதற்குப் பிந்தைய பதிப்பு தேவை, மேலும் இது pyarrow>=14-ஐக் கொண்டுவருகிறது. முன்கூட்டியே கட்டமைக்கப்பட்ட wheels (Prebuilt wheels), x86-64 மற்றும் arm64-ல் Linux, Apple silicon macOS, மற்றும் x86-64-ல் Windows ஆகியவற்றை ஆதரிக்கின்றன.
  2. import pyarrow as pa மற்றும் import pyarrow.parquet as pq-க்கு பிறகு, தரவை ஒருமுறை விவரிக்கவும்: schema = pa.schema([('ts', pa.timestamp('us', tz='UTC')), ('symbol', pa.string()), ('price', pa.float64())]).
  3. pq.write_table(pa.table({'ts': [d1, d2], 'symbol': ['ACME', 'ACME'], 'price': [10.0, 10.5]}, schema=schema), 'day1.parquet')-ஐப் பயன்படுத்தி உள்ளூர் கோப்பில் இரண்டு கற்பனை வரிசைகளை எழுதவும்; இதில் d1 மற்றும் d2 ஆகியவை timezone-ஐ உணர்ந்த (timezone-aware) தேதிகள் மற்றும் நேரங்கள் ஆகும்.
  4. தரவுத்தளத்தையும் அட்டவணையையும் உருவாக்கி, நேர நெடுவரிசைக்கு (time column) பெயரிடவும்: db = h5i_db.Database('pit.db', create=True), பின்னர் db.create_table('prices', schema, time_column='ts').
  5. ஒரு key-ன் கீழ் கோப்பை உள்ளீடு செய்யவும் (ingest): db.append('prices', pq.read_table('day1.parquet'), idempotency_key='load-day1'). இந்த அழைப்பு அது செய்த commit-ஐத் திருப்பித் தரும்.
  6. அதே வரியை மீண்டும் இயக்கவும். அதே key-ஐக் கொண்டு மீண்டும் இயக்கும்போது, ஏற்கனவே உருவாக்கப்பட்ட commit-ஐக் கண்டறிந்து, வரிசைகளை இரண்டாவது முறையாக எழுதாமல் "segments_added": 0-உடன் திருப்பித் தரும் என்று திட்ட ஆவணங்கள் கூறுகின்றன. மீண்டும் முயற்சிக்கும் (retry) செயலுக்கு முன்னும் பின்னும் db.versions('prices')-ஐ அச்சிட்டு, பதிப்புப் பட்டியல் மாறாமல் இருப்பதைக் கவனிக்கவும்.
  7. idempotency_key='load-day2'-ன் கீழ் இரண்டாவது நாள் தரவை உள்ளீடு செய்து, இரண்டையும் சேர்த்து query செய்யவும்: db.sql('SELECT symbol, count(*) AS n, avg(price) AS px FROM prices GROUP BY symbol').to_pandas().
  8. இரண்டாவது நாள் தரவு சேர்வதற்கு முன்பு இருந்த நிலையில் அட்டவணையைப் படிக்கவும்: db.read('prices', version=1). அதே பணியைச் செய்ய இந்த முறை as_of= மற்றும் snapshot= ஆகிய arguments-களை எடுத்துக்கொள்கிறது.

படி 6-ஐக் கவனமாகப் பார்க்க வேண்டும். நகல் சேர்க்கை (duplicated append) எந்தப் பிழையையும் ஏற்படுத்தாது. இது அந்த தருணத்திலிருந்து அட்டவணையைத் தவறாக மாற்றும், அதன் பிறகு நடக்கும் ஒவ்வொரு இயக்கமும் அந்தப் பாதிப்பை அமைதியாகத் தொடரும். படி 8-ன் பலன் இதுதான்: மார்ச் மாதம் கணக்கிடப்பட்ட ஒரு எண்ணை, அதே பதிப்பைக் கொண்டு ஆகஸ்ட் மாதத்திலும் மீண்டும் கணக்கிட முடியும். இதுவே மீண்டும் உருவாக்கக்கூடிய backtest குறித்த எங்கள் கட்டுரையில் கட்டமைப்பு அளவில் நாங்கள் முன்வைக்கும் பண்பாகும்.

திட்டத்தின் கூற்றுகள் மற்றும் நாங்கள் சரிபார்த்தவை

இந்த README ஒரு ஒப்பீட்டு அளவீட்டுடன் தொடங்குகிறது:

20 மில்லியன் வரிசைகள் கொண்ட OHLCV+VWAP rollups-களில் DuckDB மற்றும் Polars-ஐ விட 4.5 மடங்கு வேகமானது

இந்த எண்ணிக்கை திட்டத்தின் சொந்த அளவீடு ஆகும். இது ஆகஸ்ட் 2026-ல் பெறப்பட்ட h5i-db README-லிருந்து மேற்கோள் காட்டப்பட்டுள்ளது. நாங்கள் இதை இயக்கவில்லை, மேலே உள்ள எந்தவொரு தகவலும் இதைச் சார்ந்திருக்கவில்லை.

இங்கே வேகத்தை விட முதிர்ச்சியே முக்கியமானது. இந்தத் தொகுப்பு (repository) எழுதப்பட்ட நேரத்தில் 29 நட்சத்திரங்களைப் பெற்றிருந்தது மற்றும் Apache-2.0 உரிமத்தின் கீழ் 0.1.6 பதிப்பில் உள்ளது. இந்தச் சூழல், பராமரிப்பாளர்கள் எண்ணிக்கை குறைவாக இருப்பதையும், API-ல் மாற்றங்கள் ஏற்பட வாய்ப்புள்ளதையும் குறிக்கிறது. இந்த என்ஜின் அதிக சுமையின் கீழ் இயங்கியதற்கான நீண்டகால பொதுப் பதிவுகள் எதுவும் இல்லை. துல்லியமான பதிப்பைப் பயன்படுத்துவதும், தரவுத்தளத்திற்கு உள்ளீடாக வழங்கப்பட்ட parquet கோப்புகளைப் பாதுகாத்து வைப்பதும், ஒரு புதிய வெளியீட்டில் செயல்பாடு மாறினால் பழைய நிலைக்குத் திரும்புவதற்கான வழியை வழங்குகிறது. உங்கள் சொந்த மூலத் தரவு நகலைப் பராமரிப்பது, ஒரு விற்பனையாளர் உங்கள் தரவை மாற்றியமைப்பதைத் தடுக்கும் அதே பழக்கமாகும்; இதுவே பங்குச் சந்தைத் தரவுகளில் உயிர்வாழும் சார்பு (survivorship bias) குறித்த கட்டுரையின் மையக்கருத்தாகும்.

அடிக்கடி கேட்கப்படும் கேள்விகள் (FAQ)

Point-in-time தரவு என்றால் என்ன?

Point-in-time தரவு என்பது ஒவ்வொரு தகவலும் எப்போது அறியப்பட்டதோ, அந்த நேர முத்திரையுடன் (timestamp) சேமிக்கப்படும் தரவுத்தொகுப்பாகும். இதன் மூலம், கடந்த காலத்தின் எந்தவொரு தேதியிலும் என்ன தகவல்கள் தெரிந்திருந்தன என்பதை ஒரு வினவல் (query) மூலம் மீண்டும் உருவாக்க முடியும். சாதாரண "சமீபத்திய மதிப்பு" (latest value) அட்டவணைகளால் இதைச் செய்ய முடியாது, ஏனெனில் அவை இன்றைய திருத்தப்பட்ட எண்களைக் கொண்டு கடந்த காலத் தரவுகளை அழித்துவிடுகின்றன.

Versioned storage, look-ahead bias-ஐ நீக்குகிறதா?

இல்லை. Versioning என்பது ஒரு வகை தரவு கசிவை மட்டுமே சரிசெய்கிறது; அதாவது, ஒரு உருவகப்படுத்துதல் (simulation) முடிவு எடுக்கப்பட்ட நேரத்திற்குப் பிறகு எழுதப்பட்ட மதிப்புகளை வாசிப்பதைத் தடுக்கிறது. இருப்பினும், முழு வரலாற்றின் புள்ளிவிவரங்களைக் கொண்டு ஒரு மாதிரியை அளவிடுவது (scaling) போன்ற பிற வழிகளில் அம்ச உருவாக்கத்தின் (feature construction) போது தரவு கசிவு ஏற்பட வாய்ப்புள்ளது.

தரவை உள்ளிடும்போது (ingest) idempotency key என்ன செய்கிறது?

இது ஒரு எழுதும் செயலை (write) அடையாளப்படுத்துகிறது, இதன் மூலம் மீண்டும் செய்யப்படும் முயற்சி (retry) அதே எழுதும் செயலாகவே அங்கீகரிக்கப்படும். இதனால், ஒரு செயலி செயலிழந்த பிறகு மீண்டும் இயங்கும்போது, ஒரே வரிசைகளை இரண்டு முறை சேர்க்காமல் தடுக்க முடியும். இதுவே அட்டவணையில் தவறான தரவுகள் அமைதியாகப் பதிவாவதைத் தவிர்க்கும் வழியாகும்.

h5i-db பயன்பாட்டிற்குத் தயாரா?

இது தற்போது 0.1.6 பதிப்பில் உள்ளது மற்றும் எழுதும் நேரத்தில் GitHub-இல் 29 நட்சத்திரங்களைப் பெற்றுள்ளது; இது Apache-2.0 உரிமத்தின் கீழ் உள்ளது. இத்தகைய ஆரம்பக்கட்ட மென்பொருட்களில் API மாற்றங்கள் அடிக்கடி நிகழலாம் மற்றும் பொதுவான பயன்பாட்டு வரலாறு குறைவாகவே இருக்கும். எனவே, ஒரு பதிப்பை உறுதிப்படுத்துவதும் (version pin), மூலக் கோப்புகளின் சொந்த நகலை வைத்திருப்பதும் மட்டுமே உங்கள் மதிப்பீட்டை மாற்றக்கூடியதாக (reversible) வைத்திருக்க உதவும்.


மேலே உள்ள ஒவ்வொரு பேனலும் அதை உருவாக்கிய SQL குறியீட்டுடன் வருகிறது; எனவே, ஏதேனும் ஒன்றைத் திறந்து அந்த எண் எவ்வாறு கணக்கிடப்பட்டது என்பதைப் படியுங்கள். இதே கேள்விகளை Strasmore முனையத்தில் (terminal) எளிய ஆங்கிலத்தில் கேட்கலாம்.