def _create_lagged_data_by_moving_window(
    target_series: Optional[TimeSeries],
    output_chunk_length: int,
    output_chunk_shift: int,
    past_covariates: Optional[TimeSeries],
    future_covariates: Optional[TimeSeries],
    sample_weight: Optional[TimeSeries],
    lags: Optional[Union[Sequence[int], dict[str, list[int]]]],
    lags_past_covariates: Optional[Union[Sequence[int], dict[str, list[int]]]],
    lags_future_covariates: Optional[Union[Sequence[int], dict[str, list[int]]]],
    lags_extract: list[Optional[np.ndarray]],
    lags_order: list[Optional[np.ndarray]],
    max_samples_per_ts: Optional[int],
    multi_models: bool,
    check_inputs: bool,
    is_training: bool,
    stride: int,
    show_warnings: bool = True,
) -> tuple[np.ndarray, Optional[np.ndarray], pd.Index, Optional[np.ndarray]]:
    """
    Helper function called by `create_lagged_data` that computes `X`, `y`, and `times` by
    extracting 'moving windows' from each series using the `strided_moving_window`
    function. More specifically, to extract the features of a particular series for an
    arbitrary time `t`, a 'window' between times `t - max_lag` and `t - min_lag` is
    extracted, where `max_lag` and `min_lag` are the largest and smallest magnitude lags
    requested for that particular series. After extracting this window, the requested lag
    values between these two minimum and maximum lag values can be extracted. Similarly,
    the labels for time `t` are formed simply by extracting a window between times `t`
    and `t + output_chunk_length - 1` from the target series. In both cases, the extracted
    windows can then be reshaped into the correct shape. This approach can only be used if
    we *can* assume that the specified series are all of the same frequency.

    Assumes that all the lags are sorted in ascending order.
    """
    feature_times, min_lags, max_lags = _get_feature_times(
        target_series=target_series,
        past_covariates=past_covariates,
        future_covariates=future_covariates,
        lags=lags,
        lags_past_covariates=lags_past_covariates,
        lags_future_covariates=lags_future_covariates,
        output_chunk_length=output_chunk_length,
        output_chunk_shift=output_chunk_shift,
        is_training=is_training,
        return_min_and_max_lags=True,
        check_inputs=check_inputs,
        show_warnings=show_warnings,
    )
    if check_inputs:
        series_and_lags_not_specified = [max_lag is None for max_lag in max_lags]
        if all(series_and_lags_not_specified):
            raise_log(
                ValueError("Must specify at least one series-lags pair."), logger=logger
            )
        if not (isinstance(stride, int) and stride > 0):
            raise_log(
                ValueError("`stride` must be a positive integer greater than 0."),
                logger=logger,
            )
    sample_weight_vals = _extract_sample_weight(sample_weight, target_series)

    time_bounds = get_shared_times_bounds(*feature_times)
    if time_bounds is None:
        raise_log(
            ValueError(
                "Specified series do not share any common times for which features can be created."
            ),
            logger=logger,
        )
    freq = _get_freqs(target_series, past_covariates, future_covariates)[0]
    if isinstance(time_bounds[0], int):
        # `stop` is exclusive, so need `+ freq` to include end-point:
        times = pd.RangeIndex(
            start=time_bounds[0], stop=time_bounds[1] + freq, step=freq
        )
    else:
        times = pd.date_range(start=time_bounds[0], end=time_bounds[1], freq=freq)

    if stride > 1:
        # calculate the starting index so that the last element is included after applying the stride
        # equivalent to times[::-stride][::-1]
        times = times[(len(times) - 1) % stride :: stride]
    num_samples = len(times)
    if num_samples > max_samples_per_ts:
        times = times[-max_samples_per_ts:]
        num_samples = max_samples_per_ts
    # Time index of 'earliest' constructed observation:
    start_time = times[0]
    # Construct features array X:
    X = []
    start_time_idx = None
    target_start_time_idx = None
    for i, (series_i, lags_extract_i, lags_order_i, min_lag_i, max_lag_i) in enumerate(
        zip(
            [target_series, past_covariates, future_covariates],
            lags_extract,
            lags_order,
            min_lags,
            max_lags,
        )
    ):
        series_and_lags_specified = min_lag_i is not None
        is_target_series = is_training and (i == 0)
        if is_target_series or series_and_lags_specified:
            # get the position of `start_time` relative to the beginning of the current series
            start_time_idx = n_steps_between(
                end=start_time, start=series_i._time_index[0], freq=series_i.freq
            )
        if series_and_lags_specified:
            # Windows taken between times `t - max_lag_i` and `t - min_lag_i`
            window_len = max_lag_i - min_lag_i + 1
            first_window_start_idx = start_time_idx - max_lag_i
            first_window_end_idx = first_window_start_idx + window_len
            # Other windows are formed by sequentially shifting first window forward
            # by `stride` position each time; to create `(num_samples - 1)` more windows
            # in addition to the first window, need to take `(num_samples - 1) * stride`
            # values after `first_window_end_idx`:
            vals = series_i.all_values(copy=False)[
                first_window_start_idx : first_window_end_idx
                + (num_samples - 1) * stride,
                :,
                :,
            ]
            windows = strided_moving_window(
                x=vals, window_len=window_len, stride=stride, axis=0, check_inputs=False
            )

            # Within each window, the `-1` indexed value (i.e. the value at the very end of
            # the window) corresponds to time `t - min_lag_i`. The negative index of the time
            # `t + lag_i` within this window is, therefore, `-1 + lag_i + min_lag_i`:
            # extract lagged values
            lagged_vals = _extract_lagged_vals_from_windows(
                windows, lags_extract_i, lags_shift=min_lag_i - 1
            )
            # extract and append the reordered lagged values
            X.append(lagged_vals[:, lags_order_i])
        # Cache `start_time_idx` for label creation:
        if is_target_series:
            target_start_time_idx = start_time_idx
    X = np.concatenate(X, axis=1)
    # Construct labels array `y`:
    if is_training:
        # All values between times `t` and `t + output_chunk_length` used as labels / weights:
        # Window taken between times `t` and `t + output_chunk_length - 1`:
        first_window_start_idx = target_start_time_idx + output_chunk_shift
        # Add `+ 1` since end index is exclusive in Python:
        first_window_end_idx = (
            target_start_time_idx + output_chunk_length + output_chunk_shift
        )
        lags_to_extract = None if multi_models else -np.ones((1,), dtype=int)

        # extract target labels and sample weights
        y_and_weights = []
        for vals in [target_series.all_values(copy=False), sample_weight_vals]:
            if vals is None:
                y_and_weights.append(None)
                continue

            # To create `(num_samples - 1)` other windows in addition to first window,
            # must take `(num_samples - 1) * stride` values ahead of `first_window_end_idx`
            # to also take the stride into consideration
            vals = vals[
                first_window_start_idx : first_window_end_idx
                + (num_samples - 1) * stride,
                :,
                :,
            ]
            windows = strided_moving_window(
                x=vals,
                window_len=output_chunk_length,
                stride=stride,
                axis=0,
                check_inputs=False,
            )
            # Only values at times `t + output_chunk_length - 1` used as labels:
            vals = _extract_lagged_vals_from_windows(windows, lags_to_extract)
            y_and_weights.append(vals)

        y, weights = y_and_weights
    else:
        y, weights = None, None
    return X, y, times, weights

    Helper function called by `create_lagged_data` that computes `X`, `y`, and `times` by
    extracting 'moving windows' from each series using the `strided_moving_window`
    function. More specifically, to extract the features of a particular series for an
    arbitrary time `t`, a 'window' between times `t - max_lag` and `t - min_lag` is
    extracted, where `max_lag` and `min_lag` are the largest and smallest magnitude lags
    requested for that particular series. After extracting this window, the requested lag
    values between these two minimum and maximum lag values can be extracted. Similarly,
    the labels for time `t` are formed simply by extracting a window between times `t`
    and `t + output_chunk_length - 1` from the target series. In both cases, the extracted
    windows can then be reshaped into the correct shape. This approach can only be used if
    we *can* assume that the specified series are all of the same frequency.

    Assumes that all the lags are sorted in ascending order.
    
darts/utils/data/tabularization/tabularization.py