Skip to content

Results from incremental policy.execute do not match full backtest #190

@9th-Bacar

Description

@9th-Bacar

I am using cvxportfolio to predict cryptocurrency positions. My goal is to use selected parameters, constraints, and a cov file to predict these positions.

The project is currently under development, and my trading frequency is minute-level. I have 24 hours of returns data for ten trading pairs. I have tried the following steps:

Backtest with the first 12 hours of data, and use the holdings h from the backtest results as the initial holdings.
Subsequently, for each time step, input the latest returns data and execute the policy.execute method.
Use the resulting holdings h_plus as the initial holdings for the next execution.
However, I have found that the results obtained by iteratively executing policy.execute are not consistent with the results obtained from a full backtest using the complete 24-hour data over the same time period. There doesn't seem to be any correlation between the two. This issue has been troubling me for many days, and I hope you can provide some assistance. Thank you very much!

Partial code will be provided below.

def execute_strategy(current_holdings, market_data, policy, hyper_parameters):
    """Execute this strategy.

    :param current_holdings: Current holdings in dollars.
    :type current_holdings: pandas.Series
    :param market_data: Market data server.
    :type market_data: cvxportfolio.data.MarketData
    :param policy: Policy constructor function.
    :type policy: callable
    :param hyper_parameters: Current choice of hyper-parameters.
    :type hyper_parameters: dict

    :return: Output of the execute method of a Cvxportfolio policy.
    :rtype: tuple
    """
    _policy, _ = policy(**hyper_parameters)
    return _policy.execute(h=current_holdings, market_data=market_data)


my_returns, my_my_forecast_df = get_data(mins=1440, mode="head")
my_market_data = cvx.UserProvidedMarketData(
    returns=my_returns_df,
    min_history=pd.Timedelta("1m"),
    cash_key="cash",
)
full_policy = cvx.SinglePeriodOptimization(
    objective=(
        cvx.ReturnsForecast(r_hat=my_forecast_df)
        - config["GAMMA_RISK"] * cvx.FullCovariance(Sigma=my_cov)
        - config["GAMMA_TRADE"] * cvx.TransactionCost(a=config["FEE_RATE"], b=None)
        - config["GAMMA_DN"] * cvx.SoftConstraint(cvx.DollarNeutral())
        - config["GAMMA_HOLD"]
        * cvx.HoldingCost(
            short_fees=config["HLD_RATE"],
            long_fees=config["HLD_RATE"],
            periods_per_year=365 * 3,
        )
    ),
    constraints=[
        cvx.LeverageLimit(config["LEVERAGE_LIMIT"]),
        cvx.MaxWeights(MAX_WEIGHT),
        cvx.MinWeights(-MAX_WEIGHT),
    ],
    ignore_dpp=True,
)
full_simulator = cvx.MarketSimulator(
    market_data=my_market_data,
    cash_key="cash",
    costs=[
        cvx.TransactionCost(a=config["FEE_RATE_ACTUAL"], b=None),
        cvx.HoldingCost(short_fees=0, long_fees=0, periods_per_year=365 * 3),
    ],
    min_history=pd.Timedelta("1m"),
)

full_result = full_simulator.backtest(full_policy)
full_result.w.to_csv("full_w.csv")

my_returns, my_my_forecast_df = get_data(mins=720, mode="head")
my_market_data = cvx.UserProvidedMarketData(
    returns=my_returns_df,
    min_history=pd.Timedelta("1m"),
    cash_key="cash",
)

half_policy = cvx.SinglePeriodOptimization(
    objective=(
        cvx.ReturnsForecast(r_hat=my_forecast_df)
        - config["GAMMA_RISK"] * cvx.FullCovariance(Sigma=my_cov)
        - config["GAMMA_TRADE"] * cvx.TransactionCost(a=config["FEE_RATE"], b=None)
        - config["GAMMA_DN"] * cvx.SoftConstraint(cvx.DollarNeutral())
        - config["GAMMA_HOLD"]
        * cvx.HoldingCost(
            short_fees=config["HLD_RATE"],
            long_fees=config["HLD_RATE"],
            periods_per_year=365 * 3,
        )
    ),
    constraints=[
        cvx.LeverageLimit(config["LEVERAGE_LIMIT"]),
        cvx.MaxWeights(MAX_WEIGHT),
        cvx.MinWeights(-MAX_WEIGHT),
    ],
    ignore_dpp=True,
)
half_simulator = cvx.MarketSimulator(
    market_data=my_market_data,
    cash_key="cash",
    costs=[
        cvx.TransactionCost(a=config["FEE_RATE_ACTUAL"], b=None),
        cvx.HoldingCost(short_fees=0, long_fees=0, periods_per_year=365 * 3),
    ],
    min_history=pd.Timedelta("1m"),
)

half_result = half_simulator.backtest(half_policy)
half_result.w.to_csv("half_w.csv")

init_h = half_result.h.iloc[-1]


for i in range(0, 720):
    my_returns, my_my_forecast_df = get_data(mins=720 + i, mode="head")
    my_market_data = cvx.UserProvidedMarketData(
        returns=my_returns_df,
        min_history=pd.Timedelta("1m"),
        cash_key="cash",
    )
    policy = cvx.SinglePeriodOptimization(
        objective=(
            cvx.ReturnsForecast(r_hat=my_forecast_df)
            - config["GAMMA_RISK"] * cvx.FullCovariance(Sigma=my_cov)
            - config["GAMMA_TRADE"] * cvx.TransactionCost(a=config["FEE_RATE"], b=None)
            - config["GAMMA_DN"] * cvx.SoftConstraint(cvx.DollarNeutral())
            - config["GAMMA_HOLD"]
            * cvx.HoldingCost(
                short_fees=config["HLD_RATE"],
                long_fees=config["HLD_RATE"],
                periods_per_year=365 * 3,
            )
        ),
        constraints=[
            cvx.LeverageLimit(config["LEVERAGE_LIMIT"]),
            cvx.MaxWeights(MAX_WEIGHT),
            cvx.MinWeights(-MAX_WEIGHT),
        ],
        ignore_dpp=True,
    )

    h = init_h
    v = sum(h)
    w = h / v

    u, t, _ = execute_strategy(
        current_holdings=h, market_data=my_market_data, policy=policy
    )

    z = u / v
    w_plus = w + z
    h_plus = h + u
    init_h = h_plus
    save_result(t, w, w_plus, h, h_plus)

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions