- Add WalkForwardResult model with train/test window metrics
- Create WalkForwardEngine that reuses existing BacktestEngine
with rolling train/test window splits
- Add POST/GET /api/backtest/{id}/walkforward endpoints
- Add Walk-forward tab to backtest detail page with parameter
controls, cumulative return chart, and window results table
- Add Alembic migration for walkforward_results table
- Add 8 unit tests for window generation logic (100 total passed)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
46 lines
1.7 KiB
Python
46 lines
1.7 KiB
Python
"""add walkforward_results table
|
|
|
|
Revision ID: 59807c4e84ee
|
|
Revises: b7c8d9e0f1a2
|
|
Create Date: 2026-03-18 22:28:53.955519
|
|
|
|
"""
|
|
from typing import Sequence, Union
|
|
|
|
from alembic import op
|
|
import sqlalchemy as sa
|
|
|
|
|
|
# revision identifiers, used by Alembic.
|
|
revision: str = '59807c4e84ee'
|
|
down_revision: Union[str, None] = 'b7c8d9e0f1a2'
|
|
branch_labels: Union[str, Sequence[str], None] = None
|
|
depends_on: Union[str, Sequence[str], None] = None
|
|
|
|
|
|
def upgrade() -> None:
|
|
# ### commands auto generated by Alembic - please adjust! ###
|
|
op.create_table('walkforward_results',
|
|
sa.Column('id', sa.Integer(), nullable=False),
|
|
sa.Column('backtest_id', sa.Integer(), nullable=False),
|
|
sa.Column('window_index', sa.Integer(), nullable=False),
|
|
sa.Column('train_start', sa.Date(), nullable=False),
|
|
sa.Column('train_end', sa.Date(), nullable=False),
|
|
sa.Column('test_start', sa.Date(), nullable=False),
|
|
sa.Column('test_end', sa.Date(), nullable=False),
|
|
sa.Column('test_return', sa.Numeric(precision=10, scale=4), nullable=True),
|
|
sa.Column('test_sharpe', sa.Numeric(precision=10, scale=4), nullable=True),
|
|
sa.Column('test_mdd', sa.Numeric(precision=10, scale=4), nullable=True),
|
|
sa.ForeignKeyConstraint(['backtest_id'], ['backtests.id'], ),
|
|
sa.PrimaryKeyConstraint('id')
|
|
)
|
|
op.create_index(op.f('ix_walkforward_results_id'), 'walkforward_results', ['id'], unique=False)
|
|
# ### end Alembic commands ###
|
|
|
|
|
|
def downgrade() -> None:
|
|
# ### commands auto generated by Alembic - please adjust! ###
|
|
op.drop_index(op.f('ix_walkforward_results_id'), table_name='walkforward_results')
|
|
op.drop_table('walkforward_results')
|
|
# ### end Alembic commands ###
|