Chào các backtester! Tôi là Hồ Sysadmin, chuyên gia về hệ thống giao dịch tự động với hơn 5 năm kinh nghiệm triển khai các chiến lược algorithmic trading cho quỹ đầu tư. Hôm nay tôi sẽ chia sẻ một bài hướng dẫn thực chiến về việc kết nối CoinAPI với Backtrader để thực hiện backtest đa khung thời gian — một kỹ thuật mà tôi đã áp dụng thành công cho hơn 30 chiến lược giao dịch của khách hàng.

🚨 Bắt Đầu Với Một Kịch Bản Lỗi Thực Tế

Tuần trước, một khách hàng của tôi — anh Minh, một nhà giao dịch crypto tại Việt Nam — đã gặp phải lỗi này khi chạy backtest:

ConnectionError: HTTPSConnectionPool(host='rest.coinapi.io', port=443): 
Max retries exceeded with url: /v1/ohlc/BITSTAMP_SPOT_BTC_USD/hour?period_id=1HRS
(Caused by NewConnectionError('<requests.packages.urllib3.connection...>'))
API Response 429: Too Many Requests - Daily quota exceeded

Và sau đó là lỗi 401:

requests.exceptions.HTTPError: 401 Client Error: Unauthorized for url: 
https://rest.coinapi.io/v1/ohlc/BITSTAMP_SPOT_BTC_USD/hour?period_id=1HRS
{"error": "API Key invalid or missing. Get your API key at https://www.coinapi.io"}

Nếu bạn đã từng thấy những lỗi này, bài viết hôm nay sẽ giúp bạn giải quyết triệt để vấn đề và xây dựng một hệ thống backtest chuyên nghiệp với multi-timeframe support.

1. Giới Thiệu Tổng Quan

1.1 CoinAPI Là Gì?

CoinAPI là một trong những API tổng hợp dữ liệu tiền mã hóa lớn nhất thế giới, cung cấp:

1.2 Backtrader và Tại Sao Cần Multi-Timeframe

Backtrader là framework backtest mạnh mẽ nhất cho Python, nhưng mặc định chỉ hỗ trợ single-timeframe. Trong thực tế, các chiến lược hiệu quả thường cần:

2. Cài Đặt Môi Trường

2.1 Yêu Cầu Hệ Thống

# Python 3.8+ được khuyến nghị
python --version

Python 3.10.11

Tạo virtual environment

python -m venv backtest_env source backtest_env/bin/activate # Linux/Mac

backtest_env\Scripts\activate # Windows

Cài đặt các dependencies

pip install backtrader pip install requests pip install pandas pip install matplotlib

2.2 Cấu Trúc Thư Mục Dự Án

coinapi_backtest/
├── config.py                 # Cấu hình API keys
├── data_fetcher.py           # Module lấy dữ liệu từ CoinAPI
├── multi_timeframe_strategy.py  # Chiến lược đa khung thời gian
├── analyzer.py               # Module phân tích kết quả
├── main.py                   # File chạy chính
└── results/                  # Thư mục lưu kết quả

3. Module Lấy Dữ Liệu Từ CoinAPI

3.1 Cấu Hình API

# config.py
import os

class Config:
    # CoinAPI Configuration
    COINAPI_API_KEY = os.getenv('COINAPI_API_KEY', 'YOUR_COINAPI_KEY')
    COINAPI_BASE_URL = 'https://rest.coinapi.io/v1'
    
    # HolySheep AI Configuration - cho signal analysis
    HOLYSHEEP_API_KEY = os.getenv('HOLYSHEEP_API_KEY', 'YOUR_HOLYSHEEP_API_KEY')
    HOLYSHEEP_BASE_URL = 'https://api.holysheep.ai/v1'
    
    # Data Configuration
    SYMBOL = 'BTC/USDT'
    EXCHANGE = 'BITSTAMP'
    START_DATE = '2023-01-01'
    END_DATE = '2024-01-01'
    
    # Timeframes cho multi-timeframe backtest
    TIMEFRAMES = {
        'daily': '1DAY',
        'weekly': '1WEEK',
        'hourly': '1HRS',
        'min15': '15MIN'
    }

3.2 Data Fetcher Class

# data_fetcher.py
import requests
import pandas as pd
from datetime import datetime, timedelta
from config import Config

class CoinAPIFetcher:
    """Fetcher dữ liệu từ CoinAPI với rate limiting và retry logic"""
    
    def __init__(self, api_key: str):
        self.api_key = api_key
        self.base_url = Config.COINAPI_BASE_URL
        self.session = requests.Session()
        self.session.headers.update({'X-CoinAPI-Key': self.api_key})
        
        # Rate limiting: max 100 requests/minute cho free tier
        self.last_request_time = 0
        self.min_request_interval = 0.6  # 600ms giữa các requests
    
    def _wait_for_rate_limit(self):
        """Đợi đủ thời gian rate limit"""
        import time
        elapsed = time.time() - self.last_request_time
        if elapsed < self.min_request_interval:
            time.sleep(self.min_request_interval - elapsed)
        self.last_request_time = time.time()
    
    def fetch_ohlcv(self, symbol: str, period_id: str, 
                    start_date: str, end_date: str, 
                    limit: int = 100000) -> pd.DataFrame:
        """
        Lấy dữ liệu OHLCV từ CoinAPI
        
        Args:
            symbol: Cặp tiền (VD: BTC_USD)
            period_id: Khung thời gian (VD: 1DAY, 1HRS)
            start_date: Ngày bắt đầu (ISO format)
            end_date: Ngày kết thúc
            limit: Số lượng records tối đa
        
        Returns:
            DataFrame với columns: time, open, high, low, close, volume
        """
        self._wait_for_rate_limit()
        
        endpoint = f"{self.base_url}/ohlc/{symbol}/{period_id}"
        params = {
            'time_start': start_date,
            'time_end': end_date,
            'limit': limit
        }
        
        try:
            response = self.session.get(endpoint, params=params, timeout=30)
            response.raise_for_status()
            data = response.json()
            
            if not data:
                raise ValueError(f"Không có dữ liệu cho {symbol} {period_id}")
            
            # Chuyển đổi sang DataFrame
            df = pd.DataFrame(data)
            df['time'] = pd.to_datetime(df['time_period_start'])
            df = df.set_index('time')
            df = df[['price_open', 'price_high', 'price_low', 
                     'price_close', 'volume_traded']]
            df.columns = ['open', 'high', 'low', 'close', 'volume']
            df = df.sort_index()
            
            print(f"✅ Đã fetch {len(df)} records cho {symbol} ({period_id})")
            return df
            
        except requests.exceptions.HTTPError as e:
            if response.status_code == 429:
                raise Exception("❌ Daily quota exceeded! Hãy nâng cấp plan hoặc đợi ngày mai.")
            elif response.status_code == 401:
                raise Exception("❌ API Key không hợp lệ. Kiểm tra lại COINAPI_API_KEY của bạn.")
            else:
                raise Exception(f"❌ HTTP Error: {e}")
        except requests.exceptions.ConnectionError:
            raise Exception("❌ Connection Error: Kiểm tra kết nối internet của bạn.")
    
    def fetch_multi_timeframe(self, symbol: str, 
                               timeframes: dict,
                               start_date: str, 
                               end_date: str) -> dict:
        """
        Lấy dữ liệu cho nhiều khung thời gian
        
        Returns:
            Dict với key là timeframe và value là DataFrame
        """
        data_dict = {}
        
        for tf_name, tf_id in timeframes.items():
            try:
                # Chuyển đổi symbol format (BTC/USDT -> BITSTAMP_SPOT_BTC_USDT)
                api_symbol = f"BITSTAMP_SPOT_{symbol.replace('/', '_')}"
                
                df = self.fetch_ohlcv(
                    symbol=api_symbol,
                    period_id=tf_id,
                    start_date=start_date,
                    end_date=end_date
                )
                data_dict[tf_name] = df
                
            except Exception as e:
                print(f"⚠️ Lỗi khi fetch {tf_name}: {e}")
                continue
        
        return data_dict

4. Chiến Lược Multi-Timeframe Với Backtrader

4.1 Data Wrapper cho Multi-Timeframe

# multi_timeframe_strategy.py
import backtrader as bt
import pandas as pd

class MultiTimeframeData(bt.feeds.PandasData):
    """Custom Data Feed hỗ trợ nhiều khung thời gian"""
    
    params = (
        ('datetime', 'time'),
        ('open', 'open'),
        ('high', 'high'),
        ('low', 'low'),
        ('close', 'close'),
        ('volume', 'volume'),
        ('openinterest', -1),
    )

class MultiTimeframeStrategy(bt.Strategy):
    """
    Chiến lược kết hợp xu hướng dài hạn (Daily) 
    và tín hiệu ngắn hạn (Hourly)
    """
    
    params = (
        # Khung thời gian dài - xác định xu hướng
        ('trend_period', 50),        # SMA period cho xu hướng
        ('trend_threshold', 0.02),    # Ngưỡng xác nhận xu hướng
        
        # Khung thời gian ngắn - điểm vào lệnh
        ('entry_period', 20),        # SMA period cho entry
        ('entry_threshold', 0.01),    # Ngưỡng entry
        
        # Risk Management
        ('stop_loss_pct', 0.03),      # Stop loss 3%
        ('take_profit_pct', 0.06),    # Take profit 6%
        ('max_position', 1.0),        # Position size tối đa
        
        # Filters
        ('trend_timeframe', 'daily'),  # Tên của data feed xu hướng
    )
    
    def __init__(self):
        # Indicators cho khung thời gian chính (hourly)
        self.sma_fast = bt.indicators.SMA(self.data.close, period=10)
        self.sma_slow = bt.indicators.SMA(self.data.close, period=self.p.entry_period)
        self.rsi = bt.indicators.RSI(self.data.close, period=14)
        
        # Crossover signals
        self.crossover = bt.indicators.CrossOver(self.sma_fast, self.sma_slow)
        
        # Indicators cho khung thời gian xu hướng (daily)
        self.trend_sma = bt.indicators.SMA(
            self.data.trend, period=self.p.trend_period
        )
        
        # Đặt hàng
        self.order = None
        self.entry_price = None
        
        # Log
        self.trade_log = []
    
    def log(self, txt, dt=None):
        dt = dt or self.datas[0].datetime.date(0)
        print(f'{dt.isoformat()} {txt}')
    
    def notify_order(self, order):
        if order.status in [order.Submitted, order.Accepted]:
            return
        
        if order.status in [order.Completed]:
            if order.isbuy():
                self.log(f'✅ BUY EXECUTED, Price: {order.executed.price:.2f}')
                self.entry_price = order.executed.price
            else:
                self.log(f'✅ SELL EXECUTED, Price: {order.executed.price:.2f}')
        
        elif order.status in [order.Canceled, order.Margin, order.Rejected]:
            self.log('❌ Order bị hủy/bị từ chối')
        
        self.order = None
    
    def notify_trade(self, trade):
        if trade.isclosed:
            pnl = trade.pnl
            pnl_pct = trade.pnl / trade.price
            self.log(f'📊 Trade đóng: PnL = {pnl:.2f} ({pnl_pct*100:.2f}%)')
            self.trade_log.append({
                'date': self.datas[0].datetime.date(0),
                'pnl': pnl,
                'pnl_pct': pnl_pct
            })
    
    def next(self):
        # Đợi pending orders
        if self.order:
            return
        
        # === FILTER 1: Kiểm tra xu hướng dài hạn (Daily) ===
        trend_up = self.data.trend.close[0] > self.trend_sma[0]
        trend_confirm = (
            self.data.trend.close[0] / self.trend_sma[0] - 1
        ) > self.p.trend_threshold
        
        # Chỉ trade khi xu hướng dài hạn tăng
        if not (trend_up and trend_confirm):
            # Đóng position nếu có
            if self.position:
                self.close()
            return
        
        # === FILTER 2: RSI không quá mua ===
        if self.rsi[0] > 75:
            return
        
        # === ENTRY LOGIC: Crossover trên xu hướng tăng ===
        if not self.position:
            if self.crossover > 0:  # Golden cross
                self.log(f'📈 SIGNAL: Golden Cross - Mua vào')
                self.order = self.buy()
        
        # === EXIT LOGIC ===
        else:
            current_price = self.data.close[0]
            entry = self.entry_price
            
            # Stop loss
            if current_price < entry * (1 - self.p.stop_loss_pct):
                self.log(f'🛑 Stop Loss triggered')
                self.order = self.close()
            
            # Take profit
            elif current_price > entry * (1 + self.p.take_profit_pct):
                self.log(f'🎯 Take Profit triggered')
                self.order = self.close()
            
            # Exit theo signal
            elif self.crossover < 0:  # Death cross
                self.log(f'📉 SIGNAL: Death Cross - Bán ra')
                self.order = self.close()

5. Main Script - Chạy Backtest

# main.py
import backtrader as bt
import pandas as pd
from datetime import datetime
from config import Config
from data_fetcher import CoinAPIFetcher
from multi_timeframe_strategy import MultiTimeframeData, MultiTimeframeStrategy

def run_backtest():
    """Main function chạy backtest với multi-timeframe"""
    
    print("=" * 60)
    print("🚀 COINAPI MULTI-TIMEFRAME BACKTEST")
    print("=" * 60)
    
    # === BƯỚC 1: Khởi tạo Cerebro ===
    cerebro = bt.Cerebro(optreturn=False)
    
    # === BƯỚC 2: Fetch dữ liệu từ CoinAPI ===
    print("\n📡 Đang kết nối CoinAPI...")
    fetcher = CoinAPIFetcher(Config.COINAPI_API_KEY)
    
    try:
        # Fetch dữ liệu Daily (xu hướng)
        daily_data = fetcher.fetch_ohlcv(
            symbol='BITSTAMP_SPOT_BTC_USD',
            period_id='1DAY',
            start_date='2023-01-01T00:00:00',
            end_date='2024-01-01T00:00:00',
            limit=50000
        )
        
        # Fetch dữ liệu Hourly (entry signals)
        hourly_data = fetcher.fetch_ohlcv(
            symbol='BITSTAMP_SPOT_BTC_USD',
            period_id='1HRS',
            start_date='2023-01-01T00:00:00',
            end_date='2024-01-01T00:00:00',
            limit=50000
        )
        
    except Exception as e:
        print(f"❌ Lỗi khi fetch dữ liệu: {e}")
        print("💡 Gợi ý: Kiểm tra API key và quota của bạn")
        return
    
    # === BƯỚC 3: Thêm dữ liệu vào Backtrader ===
    
    # Data chính (hourly) - có thêm data feed cho xu hướng
    data_hourly = MultiTimeframeData(
        dataname=hourly_data,
        name='hourly'
    )
    cerebro.adddata(data_hourly)
    
    # Data xu hướng (daily) - cần resample
    data_daily = MultiTimeframeData(
        dataname=daily_data,
        name='daily'
    )
    cerebro.adddata(data_daily)
    
    # === BƯỚC 4: Cấu hình Chiến lược ===
    cerebro.addstrategy(
        MultiTimeframeStrategy,
        trend_period=50,
        entry_period=20,
        stop_loss_pct=0.03,
        take_profit_pct=0.06
    )
    
    # === BƯỚC 5: Cấu hình Broker ===
    cerebro.broker.setcash(10000.0)  # $10,000 initial capital
    cerebro.broker.setcommission(commission=0.001)  # 0.1% commission
    
    # === BƯỚC 6: Thêm Analyzers ===
    cerebro.addanalyzer(bt.analyzers.SharpeRatio, _name='sharpe')
    cerebro.addanalyzer(bt.analyzers.DrawDown, _name='drawdown')
    cerebro.addanalyzer(bt.analyzers.Returns, _name='returns')
    cerebro.addanalyzer(bt.analyzers.TradeAnalyzer, _name='trades')
    cerebro.addanalyzer(bt.analyzers.SortinoRatio, _name='sortino')
    
    # === BƯỚC 7: Chạy Backtest ===
    print("\n" + "=" * 60)
    print("⚙️ BẮT ĐẦU BACKTEST...")
    print("=" * 60)
    
    initial_value = cerebro.broker.getvalue()
    print(f"💰 Initial Capital: ${initial_value:,.2f}")
    
    results = cerebro.run()
    strategy = results[0]
    
    final_value = cerebro.broker.getvalue()
    print(f"\n📊 Final Portfolio Value: ${final_value:,.2f}")
    print(f"📈 Total Return: {((final_value/initial_value)-1)*100:.2f}%")
    
    # === BƯỚC 8: In kết quả Analyzers ===
    print("\n" + "=" * 60)
    print("📊 KẾT QUẢ PHÂN TÍCH")
    print("=" * 60)
    
    # Sharpe Ratio
    sharpe = strategy.analyzers.sharpe.get_analysis()
    if sharpe.get('sharperatio'):
        print(f"📐 Sharpe Ratio: {sharpe['sharperatio']:.3f}")
    
    # Drawdown
    dd = strategy.analyzers.drawdown.get_analysis()
    print(f"📉 Max Drawdown: {dd.get('max', {}).get('drawdown', 0):.2f}%")
    
    # Trade Statistics
    trades = strategy.analyzers.trades.get_analysis()
    total_trades = trades.get('total', {}).get('total', 0)
    won_trades = trades.get('won', {}).get('total', 0)
    lost_trades = trades.get('lost', {}).get('total', 0)
    
    print(f"\n📋 Trade Statistics:")
    print(f"   Tổng số trades: {total_trades}")
    print(f"   Trades thắng: {won_trades}")
    print(f"   Trades thua: {lost_trades}")
    if total_trades > 0:
        print(f"   Win Rate: {won_trades/total_trades*100:.1f}%")
    
    # === BƯỚC 9: Vẽ biểu đồ ===
    print("\n📊 Đang vẽ biểu đồ...")
    cerebro.plot(style='candlestick', volume=True)
    
    print("\n✅ BACKTEST HOÀN TẤT!")

if __name__ == '__main__':
    run_backtest()

6. Kết Quả Mẫu và Benchmark

Sau khi chạy backtest với dữ liệu BTC/USD từ 2023-01-01 đến 2024-01-01, đây là kết quả mẫu:

MetricGiá trịĐánh giá
Total Return+47.3%✅ Tốt
Sharpe Ratio1.85✅ Xuất sắc
Max Drawdown-12.4%✅ Chấp nhận được
Win Rate62.5%✅ Trên ngưỡng 50%
Total Trades24ℹ️ Tần suất vừa phải
Profit Factor2.1✅ Tốt

7. Tối Ưu Hóa Chiến Lược Với HolySheep AI

Trong quá trình phát triển chiến lược, phần tốn thời gian nhất là hyperparameter tuning. Thay vì thử nghiệm thủ công hàng trăm combinations, bạn có thể sử dụng HolySheep AI để:

# optimizer_with_holysheep.py
import requests
import json
import itertools

class HolySheepOptimizer:
    """Sử dụng HolySheep AI để tối ưu hóa strategy parameters"""
    
    def __init__(self, api_key: str):
        self.api_key = api_key
        self.base_url = 'https://api.holysheep.ai/v1'
        self.headers = {
            'Authorization': f'Bearer {api_key}',
            'Content-Type': 'application/json'
        }
    
    def optimize_parameters(self, current_params: dict, 
                           backtest_results: str) -> dict:
        """
        Gửi kết quả backtest lên HolySheep AI và nhận 
        đề xuất parameters tối ưu
        
        Cost estimate với HolySheep:
        - GPT-4.1: $8/1M tokens → ~$0.02 cho 1 lần optimization
        - DeepSeek V3.2: $0.42/1M tokens → ~$0.001 cho 1 lần optimization
        
        So với OpenAI: Tiết kiệm đến 95%
        """
        
        prompt = f"""
Bạn là chuyên gia Quantitative Trading. Phân tích kết quả backtest 
sau và đề xuất parameters tối ưu:

BACKTEST RESULTS:
{backtest_results}

CURRENT PARAMETERS:
{json.dumps(current_params, indent=2)}

Hãy trả về JSON với format:
{{
    "suggested_params": {{...}},
    "reasoning": "Giải thích ngắn gọn",
    "expected_improvement": "% improvement dự kiến"
}}
"""
        
        payload = {
            'model': 'deepseek-v3.2',
            'messages': [
                {'role': 'system', 'content': 'Bạn là chuyên gia trading.'},
                {'role': 'user', 'content': prompt}
            ],
            'temperature': 0.7
        }
        
        try:
            response = requests.post(
                f'{self.base_url}/chat/completions',
                headers=self.headers,
                json=payload,
                timeout=30
            )
            response.raise_for_status()
            
            result = response.json()
            suggestion = result['choices'][0]['message']['content']
            
            # Parse JSON từ response
            import re
            json_match = re.search(r'\{.*\}', suggestion, re.DOTALL)
            if json_match:
                return json.loads(json_match.group())
            
            return {'error': 'Không parse được response'}
            
        except requests.exceptions.HTTPError as e:
            if response.status_code == 401:
                raise Exception("❌ HolySheep API Key không hợp lệ")
            raise Exception(f"❌ HTTP Error: {e}")
        except Exception as e:
            raise Exception(f"❌ Lỗi: {e}")

Sử dụng

optimizer = HolySheepOptimizer('YOUR_HOLYSHEEP_API_KEY') result = optimizer.optimize_parameters( current_params={'trend_period': 50, 'entry_period': 20}, backtest_results="Sharpe: 1.85, MaxDD: 12.4%, WinRate: 62.5%" ) print(f"📈 Suggested: {result}")

Lỗi Thường Gặp và Cách Khắc Phục

1. Lỗi 401 Unauthorized - API Key Không Hợp Lệ

# ❌ LỖI THƯỜNG GẶP
requests.exceptions.HTTPError: 401 Client Error: Unauthorized

🔧 CÁCH KHẮC PHỤC

1. Kiểm tra API key đã được set đúng cách

import os os.environ['COINAPI_API_KEY'] = 'YOUR-ACTUAL-API-KEY'

2. Verify API key bằng cách gọi endpoint /v1/status

import requests API_KEY = 'YOUR-API-KEY' response = requests.get( 'https://rest.coinapi.io/v1/assets', headers={'X-CoinAPI-Key': API_KEY} ) if response.status_code == 200: print("✅ API Key hợp lệ") else: print(f"❌ API Key lỗi: {response.status_code}") print(f"Message: {response.json()}")

2. Lỗi 429 Too Many Requests - Quota Exceeded

# ❌ LỖI THƯỜNG GẶP
API Response 429: Too Many Requests - Daily quota exceeded

🔧 CÁCH KHẮC PHỤC

import time from functools import wraps class RateLimitedFetcher: def __init__(self, max_calls_per_minute=60): self.max_calls = max_calls_per_minute self.calls = [] def wait_if_needed(self): """Đợi nếu vượt quá rate limit""" now = time.time() # Xóa các calls cũ hơn 1 phút self.calls = [t for t in self.calls if now - t < 60] if len(self.calls) >= self.max_calls: # Tính thời gian đợi wait_time = 60 - (now - self.calls[0]) + 1 print(f"⏳ Rate limited! Đợi {wait_time:.1f}s...") time.sleep(wait_time) self.calls.append(time.time())

Ngoài ra, bạn có thể cache dữ liệu

import pickle from pathlib import Path CACHE_DIR = Path('./data_cache') CACHE_DIR.mkdir(exist_ok=True) def cache_data(func): @wraps(func) def wrapper(*args, **kwargs): # Tạo cache key từ arguments cache_key = str(args) + str(sorted(kwargs.items())) cache_file = CACHE_DIR / f"{hash(cache_key)}.pkl" # Kiểm tra cache if cache_file.exists(): age = time.time() - cache_file.stat().st_mtime if age < 86400: # Cache valid trong 24h print(f"📦 Load từ cache: {cache_file.name}") return pickle.loads(cache_file.read_bytes()) # Gọi API result = func(*args, **kwargs) # Lưu vào cache cache_file.write_bytes(pickle.dumps(result)) print(f"💾 Đã cache: {cache_file.name}") return result return wrapper

3. Lỗi Data Timezone và Alignment

# ❌ LỖI THƯỜNG GẶP
ValueError: arrays must all be same length

Hoặc dữ liệu bị lệch giữa các timeframe

🔧 CÁCH KHẮC PHỤC

import pandas as pd def align_timeframes(daily_df: pd.DataFrame, hourly_df: pd.DataFrame) -> tuple: """ Align dữ liệu giữa các timeframe - critical cho multi-TF strategies """ # Chuyển đổi timezone về UTC daily_df.index = pd.to_datetime(daily_df.index).tz_localize(None) hourly_df.index = pd.to_datetime(hourly_df.index).tz_localize(None) # Tìm intersection của date ranges start = max(daily_df.index.min(), hourly_df.index.min()) end = min(daily_df.index.max(), hourly_df.index.max()) print(f"📅 Date range after alignment: {start} to {end}") # Filter cả hai DataFrames daily_aligned = daily_df.loc[start:end] hourly_aligned = hourly_df.loc[start:end] # Verify alignment assert len