Navigation

    Quantiacs Community

    • Register
    • Login
    • Search
    • Categories
    • News
    • Recent
    • Tags
    • Popular
    • Users
    • Groups

    backtest_ml has too long a run time

    Strategy help
    2
    14
    873
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • illustrious.felice
      illustrious.felice @illustrious.felice last edited by

      @illustrious-felice please help me @support @Vyacheslav_B

      1 Reply Last reply Reply Quote 0
      • V
        Vyacheslav_B @illustrious.felice last edited by

        @illustrious-felice Hello.

        I do not see any issues with the specified parameters. Please note that retrain_interval_after_submit=1 is set; after
        submission to the competition, the model will retrain every day. If you plan to submit your strategy to the competition,
        it is better to rewrite it for a single-pass mode. A quick quality assessment can be made using
        precheck here.

        There are not many options for acceleration:

        • Simplifying the machine learning model.
        • Use local development here with a
          powerful PC or run the script on a powerful server.
        • Reducing the amount of data fed into the model.
        • Shortening the testing period for quick elimination of unsuccessful models. For example, you can look at the
          financial results the model shows for the last year or another period.

        You can also do some tricks with the input data, but there is a big risk of feeding future data into the model. If
        technical indicators are fed into the model, they can be calculated in advance, but internally exclude the data of
        already pre-calculated indicators.

        Here is an example of using a pre-calculated EMA g_ema = qnta.ema(data_all.sel(field="high"), 15)

        import xarray as xr
        
        import qnt.backtester as qnbt
        import qnt.data as qndata
        import numpy as np
        import logging
        import qnt.stats as qns
        import qnt.graph as qngraph
        import qnt.ta as qnta
        
        data_all = qndata.stocks.load_ndx_data(min_date="2023-01-01", assets=["NAS:AMZN"])
        g_ema = qnta.ema(data_all.sel(field="high"), 15)
        
        
        def load_data(period):
            return qndata.stocks.load_ndx_data(tail=period, assets=["NAS:AMZN"])
        
        
        def predict_weights(market_data):
            def get_ml_model():
                # you can use any machine learning model here
                from sklearn.linear_model import RidgeClassifier
                model = RidgeClassifier(random_state=18)
                return model
        
            def get_features(data):
                time_start = data.time.values[0]
                time_end = data.time.values[-1]
                ema = g_ema.sel(time=slice(time_start, time_end))
                for_result = ema.to_pandas()
                return for_result
        
            def get_target_classes(data):
                # define categorical targets
                price_current = data.sel(field="close").dropna("time")  # rm NaN
                price_future = price_current.shift(time=-1).dropna("time")
        
                class_positive = 1
                class_negative = 0
        
                target_is_price_up = xr.where(price_future > price_current, class_positive, class_negative)
                return target_is_price_up.to_pandas()
        
            data = market_data.copy(True)
        
            asset_name_all = data.coords["asset"].values
            features_all_df = get_features(data)
            target_all_df = get_target_classes(data)
        
            predict_weights_next_day_df = data.sel(field="close").isel(time=-1).to_pandas()
        
            for asset_name in asset_name_all:
                target_for_learn_df = target_all_df[asset_name]
                feature_for_learn_df = features_all_df[asset_name][:-1]  # last value reserved for prediction
        
                # align features and targets
                target_for_learn_df, feature_for_learn_df = target_for_learn_df.align(feature_for_learn_df, axis=0,
                                                                                      join="inner")
        
                model = get_ml_model()
                try:
                    model.fit(feature_for_learn_df.values.reshape(-1, 1), target_for_learn_df)
        
                    feature_for_predict_df = features_all_df[asset_name][-1:]
        
                    predict = model.predict(feature_for_predict_df.values.reshape(-1, 1))
                    predict_weights_next_day_df[asset_name] = predict
                except:
                    logging.exception("model failed")
                    # if there is exception, return zero values
                    return xr.zeros_like(data.isel(field=0, time=0))
        
            return predict_weights_next_day_df.to_xarray()
        
        
        weights = qnbt.backtest(
            competition_type="stocks_nasdaq100",
            load_data=load_data,
            lookback_period=18,
            start_date="2024-01-01",
            strategy=predict_weights,
            analyze=True,
            build_plots=True
        )
        
        

        A different approach can be taken to develop algorithmic solutions based on machine learning. Initially, one can search
        for strong machine learning models that show good accuracy, then test them using backtest_ml to discard weak models
        before backtest_ml.

        illustrious.felice 3 Replies Last reply Reply Quote 0
        • illustrious.felice
          illustrious.felice @Vyacheslav_B last edited by

          @vyacheslav_b Thank you so much

          1 Reply Last reply Reply Quote 0
          • illustrious.felice
            illustrious.felice @Vyacheslav_B last edited by

            @vyacheslav_b Thank you. Please help me with the issue RuntimeError: expand(torch.DoubleTensor{[694, 6]}, size=[694]): the number of sizes provided (1) must be greater or equal to the number of dimensions in the tensor (2).

            I have tried adding the following feature:
            Screenshot 2024-03-04 200450.png
            However I got an error
            eaff9375-5550-4632-b22b-386f04894c42-image.png
            5ef46a52-c58d-41c8-a64b-b80f4ed2b952-image.png
            Please help me fix the error. I really need to fix this bug so I can train more than 1 feature. Thank you very much.

            1 Reply Last reply Reply Quote 0
            • illustrious.felice
              illustrious.felice @Vyacheslav_B last edited by illustrious.felice

              @vyacheslav_b Hopefully you can give me a code example of LSTM when using more than 1 feature to train in the get_feaures() function and using more than 3 assets (stock codes). Thank you. Currently I am getting an error when changing the number of features and assets. Thank you so much.

              I hope you can show me an example of changing ml_backtest to single backtest in example neural networks too.

              V 1 Reply Last reply Reply Quote 0
              • V
                Vyacheslav_B @illustrious.felice last edited by

                @illustrious-felice Hello.
                This is an example of LSTM where 3 features are used for prediction: log(close), log(open), high.
                input_dim=3
                asset_name_all = ['NAS:AAPL', 'NAS:AMZN', 'NAS:MSFT']

                You need to modify the get_model and get_features functions to suit your features

                import xarray as xr  # xarray for data manipulation
                import qnt.data as qndata  # functions for loading data
                import qnt.backtester as qnbt  # built-in backtester
                import qnt.ta as qnta  # technical analysis library
                import numpy as np
                import logging
                import pandas as pd
                
                import torch
                from torch import nn, optim
                
                asset_name_all = ['NAS:AAPL', 'NAS:AMZN', 'NAS:MSFT']
                
                
                class LSTM(nn.Module):
                    """
                    Class to define our LSTM network.
                    """
                
                    def __init__(self, input_dim=2, hidden_layers=64):
                        super(LSTM, self).__init__()
                        self.hidden_layers = hidden_layers
                        self.lstm1 = nn.LSTMCell(input_dim, self.hidden_layers)
                        self.lstm2 = nn.LSTMCell(self.hidden_layers, self.hidden_layers)
                        self.linear = nn.Linear(self.hidden_layers, 1)
                
                    def forward(self, y, future_preds=0):
                        outputs = []
                        n_samples = y.size(0)
                        h_t = torch.zeros(n_samples, self.hidden_layers, dtype=torch.float32)
                        c_t = torch.zeros(n_samples, self.hidden_layers, dtype=torch.float32)
                        h_t2 = torch.zeros(n_samples, self.hidden_layers, dtype=torch.float32)
                        c_t2 = torch.zeros(n_samples, self.hidden_layers, dtype=torch.float32)
                
                        for time_step in range(y.size(1)):
                            x_t = y[:, time_step, :]  # Ensure x_t is [batch, input_dim]
                
                            h_t, c_t = self.lstm1(x_t, (h_t, c_t))
                            h_t2, c_t2 = self.lstm2(h_t, (h_t2, c_t2))
                            output = self.linear(h_t2)
                            outputs.append(output.unsqueeze(1))
                
                        outputs = torch.cat(outputs, dim=1).squeeze(-1)
                        return outputs
                
                
                def get_model():
                    model = LSTM(input_dim=3)
                    return model
                
                
                def get_features(data):
                    close_price = data.sel(field="close").ffill('time').bfill('time').fillna(1)
                    open_price = data.sel(field="open").ffill('time').bfill('time').fillna(1)
                    high_price = data.sel(field="high").ffill('time').bfill('time').fillna(1)
                    log_close = np.log(close_price)
                    log_open = np.log(open_price)
                    features = xr.concat([log_close, log_open, high_price], "feature")
                    return features
                
                
                def get_target_classes(data):
                    price_current = data.sel(field='close')
                    price_future = qnta.shift(price_current, -1)
                
                    class_positive = 1  # prices goes up
                    class_negative = 0  # price goes down
                
                    target_price_up = xr.where(price_future > price_current, class_positive, class_negative)
                
                    return target_price_up
                
                
                def load_data(period):
                    return qndata.stocks.load_ndx_data(tail=period, assets=asset_name_all)
                
                
                def train_model(data):
                    """
                        train the LSTM network
                    """
                
                    features_all = get_features(data)
                    target_all = get_target_classes(data)
                
                    models = dict()
                
                    for asset_name in asset_name_all:
                        model = get_model()
                
                        # drop missing values:
                        target_cur = target_all.sel(asset=asset_name).dropna('time', 'any')
                        features_cur = features_all.sel(asset=asset_name).dropna('time', 'any')
                
                        # align features and targets:
                        target_for_learn_df, feature_for_learn_df = xr.align(target_cur, features_cur, join='inner')
                
                        criterion = nn.MSELoss()  # define loss function
                
                        optimiser = optim.LBFGS(model.parameters(), lr=0.08)  # we use an LBFGS solver as optimiser
                        epochs = 1  # how many epochs 
                        for i in range(epochs):
                            def closure():  # reevaluates the model and returns the loss (forward pass)
                                optimiser.zero_grad()
                
                                # input tensor                     
                                feature_data = feature_for_learn_df.transpose('time', 'feature').values
                                in_ = torch.tensor(feature_data, dtype=torch.float32).unsqueeze(0)
                
                                # output
                                out = model(in_)
                
                                # target tensor
                                target = torch.zeros(1, len(target_for_learn_df.values))
                                target[0, :] = torch.tensor(np.array(target_for_learn_df.values))
                
                                # evaluate loss
                                loss = criterion(out, target)
                                loss.backward()
                
                                return loss
                
                            optimiser.step(closure)  # updates weights
                
                        models[asset_name] = model
                
                    return models
                
                
                def predict(models, data):
                    """
                        predict if price is going up or down and go long depending on it
                    
                    """
                    weights = xr.zeros_like(data.sel(field='close'))
                
                    for asset_name in asset_name_all:
                        features_all = get_features(data)
                        features_cur = features_all.sel(asset=asset_name).dropna('time', 'any')
                        if len(features_cur.time) < 1:
                            continue
                
                        # input tensor
                        feature_data = features_cur.transpose('time', 'feature').values
                        in_ = torch.tensor(feature_data, dtype=torch.float32).unsqueeze(0)
                
                        # output
                        out = models[asset_name](in_)
                
                        prediction = out.detach()[0]
                
                        weights.loc[dict(asset=asset_name, time=features_cur.time.values)] = prediction
                
                    return weights
                
                
                weights = qnbt.backtest_ml(
                    load_data=load_data,
                    train=train_model,
                    predict=predict,
                    train_period=55,  # the data length for training in calendar days
                    retrain_interval=55,  # how often we have to retrain models (calendar days)
                    retrain_interval_after_submit=1,  # how often retrain models after submission during evaluation (calendar days)
                    predict_each_day=False,  # Is it necessary to call prediction for every day during backtesting?
                    # Set it to true if you suspect that get_features is looking forward.
                    competition_type='stocks_nasdaq100',  # competition type
                    lookback_period=55,  # how many calendar days are needed by the predict function to generate the output
                    start_date='2024-01-01',  # backtest start date
                    build_plots=True  # do you need the chart?
                )
                
                
                
                illustrious.felice 1 Reply Last reply Reply Quote 0
                • illustrious.felice
                  illustrious.felice @Vyacheslav_B last edited by illustrious.felice

                  @vyacheslav_b Thank you so much. I have one more question. When I add stock (NAS:TSLA, NAS:FB,...), I get the following error:
                  423541864_1114755819833295_6385236541869982169_n.png
                  It seems that these tickers are missing data. I tried other codes and found some that could be added like ADBE, GOOGL. I would like to ask why this situation occurs and is there any way to fix it? Thank you very much.

                  V illustrious.felice 2 Replies Last reply Reply Quote 0
                  • V
                    Vyacheslav_B @illustrious.felice last edited by

                    @illustrious-felice I didn't understand what you mean.

                    standard functions allow you to fill in gaps with default values

                    data.sel(field="high").ffill('time').bfill('time').fillna(1)
                    
                    1 Reply Last reply Reply Quote 0
                    • illustrious.felice
                      illustrious.felice @illustrious.felice last edited by

                      @illustrious-felice If I leave the list of stocks as these stocks, the backtest will run normally and I will get the results
                      Screenshot 2024-03-06 220401.png Screenshot 2024-03-06 220354.png
                      However, if I change to other codes, such as TSLA, FB, META, it will error
                      Screenshot 2024-03-06 220523.png Screenshot 2024-03-06 220517.png
                      I could only find about 6 or 7 stock codes that didn't have errors

                      illustrious.felice 1 Reply Last reply Reply Quote 0
                      • illustrious.felice
                        illustrious.felice @illustrious.felice last edited by

                        @illustrious-felice I would like to ask, how can I remove the correlation check each time I run ml_backtest? Thank you so much. Checking correlation is time consuming and I want to eliminate it

                        V 1 Reply Last reply Reply Quote 0
                        • V
                          Vyacheslav_B @illustrious.felice last edited by

                          @illustrious-felice Hello.

                          Make a copy of your template in your personal account.

                          In the new version of the qnt library, there is a parameter called check_correlation, which is disabled by default.

                          I looked at the trading instruments TSLA, FB, META: in 2006, they were not traded on the stock exchange. Try adding one more ticker to the data for these instruments, which was available in 2006.

                          illustrious.felice 1 Reply Last reply Reply Quote 0
                          • illustrious.felice
                            illustrious.felice @Vyacheslav_B last edited by illustrious.felice

                            @vyacheslav_b Thank you so much. I would like to ask more, every time I train ml_backtest for LSTM, I get a different sharpe result (sometimes the sharpness is 1.2, 1.1, 0.9,...) Why is there the following sharpness inconsistency? Every time you train like that? We wish to be answered. Thank you.

                            V 1 Reply Last reply Reply Quote 0
                            • V
                              Vyacheslav_B @illustrious.felice last edited by

                              @illustrious-felice

                              Incorporating seed initialization into your PyTorch code ensures reproducibility by making the random number generation predictable. This involves setting seeds for the PyTorch engine, NumPy, and the Python random module if you're using it. Below, I'll show you how to integrate seed initialization into your existing code. Remember, while this can make your experiments more reproducible, it does not guarantee identical results across different hardware or PyTorch versions due to the inherent nondeterminism in some GPU operations.

                              import xarray as xr  # xarray for data manipulation
                              import qnt.data as qndata  # functions for loading data
                              import qnt.backtester as qnbt  # built-in backtester
                              import qnt.ta as qnta  # technical analysis library
                              import numpy as np
                              import pandas as pd
                              import torch
                              from torch import nn, optim
                              import random
                              
                              # Seed initialization function
                              def set_seed(seed_value=42):
                                  """Set seed for reproducibility."""
                                  random.seed(seed_value)
                                  np.random.seed(seed_value)
                                  torch.manual_seed(seed_value)
                                  torch.cuda.manual_seed(seed_value)
                                  torch.cuda.manual_seed_all(seed_value)  # if you are using multi-GPU.
                                  torch.backends.cudnn.deterministic = True
                                  torch.backends.cudnn.benchmark = False
                              
                              # Set the seed for reproducibility
                              set_seed(42)
                              
                              asset_name_all = ['NAS:AAPL', 'NAS:AMZN', 'NAS:MSFT']
                              
                              class LSTM(nn.Module):
                                  """
                                  Class to define our LSTM network.
                                  """
                                  def __init__(self, input_dim=3, hidden_layers=64):
                                      super(LSTM, self).__init__()
                                      self.hidden_layers = hidden_layers
                                      self.lstm1 = nn.LSTMCell(input_dim, self.hidden_layers)
                                      self.lstm2 = nn.LSTMCell(self.hidden_layers, self.hidden_layers)
                                      self.linear = nn.Linear(self.hidden_layers, 1)
                              
                                  def forward(self, y, future_preds=0):
                                      outputs = []
                                      n_samples = y.size(0)
                                      h_t = torch.zeros(n_samples, self.hidden_layers, dtype=torch.float32)
                                      c_t = torch.zeros(n_samples, self.hidden_layers, dtype=torch.float32)
                                      h_t2 = torch.zeros(n_samples, self.hidden_layers, dtype=torch.float32)
                                      c_t2 = torch.zeros(n_samples, self.hidden_layers, dtype=torch.float32)
                              
                                      for time_step in range(y.size(1)):
                                          x_t = y[:, time_step, :]  # Ensure x_t is [batch, input_dim]
                              
                                          h_t, c_t = self.lstm1(x_t, (h_t, c_t))
                                          h_t2, c_t2 = self.lstm2(h_t, (h_t2, c_t2))
                                          output = self.linear(h_t2)
                                          outputs.append(output.unsqueeze(1))
                              
                                      outputs = torch.cat(outputs, dim=1).squeeze(-1)
                                      return outputs
                              
                              def get_model():
                                  model = LSTM(input_dim=3)
                                  return model
                              
                              def get_features(data):
                                  close_price = data.sel(field="close").ffill('time').bfill('time').fillna(1)
                                  open_price = data.sel(field="open").ffill('time').bfill('time').fillna(1)
                                  high_price = data.sel(field="high").ffill('time').bfill('time').fillna(1)
                                  log_close = np.log(close_price)
                                  log_open = np.log(open_price)
                                  features = xr.concat([log_close, log_open, high_price], "feature")
                                  return features
                              
                              def get_target_classes(data):
                                  price_current = data.sel(field='close')
                                  price_future = qnta.shift(price_current, -1)
                              
                                  class_positive = 1  # prices goes up
                                  class_negative = 0  # price goes down
                              
                                  target_price_up = xr.where(price_future > price_current, class_positive, class_negative)
                                  return target_price_up
                              
                              def load_data(period):
                                  return qndata.stocks.load_ndx_data(tail=period, assets=asset_name_all)
                              
                              def train_model(data):
                                  features_all = get_features(data)
                                  target_all = get_target_classes(data)
                                  models = dict()
                              
                                  for asset_name in asset_name_all:
                                      model = get_model()
                                      target_cur = target_all.sel(asset=asset_name).dropna('time', 'any')
                                      features_cur = features_all.sel(asset=asset_name).dropna('time', 'any')
                                      target_for_learn_df, feature_for_learn_df = xr.align(target_cur, features_cur, join='inner')
                                      criterion = nn.MSELoss()
                                      optimiser = optim.LBFGS(model.parameters(), lr=0.08)
                                      epochs = 1
                                      for i in range(epochs):
                                          def closure():
                                              optimiser.zero_grad()
                                              feature_data = feature_for_learn_df.transpose('time', 'feature').values
                                              in_ = torch.tensor(feature_data, dtype=torch.float32).unsqueeze(0)
                                              out = model(in_)
                                              target = torch.zeros(1, len(target_for_learn_df.values))
                                              target[0, :] = torch.tensor(np.array(target_for_learn_df.values))
                                              loss = criterion(out, target)
                                              loss.backward()
                                              return loss
                                          optimiser.step(closure)
                                      models[asset_name] = model
                                  return models
                              
                              def predict(models, data):
                                  weights = xr.zeros_like(data.sel(field='close'))
                                  for asset_name in asset_name_all:
                                      features_all = get_features(data)
                                      features_cur = features_all.sel(asset=asset_name).dropna('time', 'any')
                                      if len(features_cur.time) < 1:
                                          continue
                                      feature_data = features_cur.transpose('time', 'feature').values
                                      in_ = torch.tensor(feature_data, dtype=torch.float32).unsqueeze(0)
                                      out = models[asset_name](in_)
                                      prediction = out.detach()[0]
                                      weights.loc[dict(asset=asset_name, time=features_cur.time.values)] = prediction
                                  return weights
                              
                              weights = qnbt.backtest_ml(
                                  load_data=load_data,
                                  train=train_model,
                                  predict=predict,
                                  train_period=55,
                                  retrain_interval=55,
                                  retrain_interval_after_submit=1,
                                  predict_each_day=False,
                                  competition_type='stocks_nasdaq100',
                                  lookback_period=55,
                                  start_date='2024-01-01',
                                  build_plots=True
                              )
                              
                              

                              I think I won't be available next week. If you have any more questions, don’t expect an answer from me next week.

                              1 Reply Last reply Reply Quote 1
                              • First post
                                Last post
                              Powered by NodeBB | Contributors
                              • Documentation
                              • About
                              • Career
                              • My account
                              • Privacy policy
                              • Terms and Conditions
                              • Cookies policy
                              Home
                              Copyright © 2014 - 2021 Quantiacs LLC.
                              Powered by NodeBB | Contributors