Navigation

    Quantiacs Community

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

    Optimizer for simple MA crypto strategy

    Strategy help
    3
    4
    273
    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.
    • magenta.grimer
      magenta.grimer last edited by

      hi!

      Can you help in running the optimizer for this simple strategy?

      import qnt.data as qndata
      import qnt.ta as qnta
      import qnt.output as qnout
      import qnt.stats as qns
      import qnt.log as qnlog
      import qnt.optimizer as qnop
      import qnt.backtester as qnbt
      
      import xarray as xr
      
      def single_pass_strategy(data, ma_slow_param=50, ma_fast_param=10):
          close= data.sel(field="close")
          
          ma_slow= qnta.lwma(close, ma_slow_param).isel(time=-1)   
          ma_fast= qnta.lwma(close, ma_fast_param).isel(time=-1)
          return xr.where(ma_fast > ma_slow, 1, -1)
      
      def load_data(period):
          """Loads the BTC Futures data for the BTC Futures contest"""
          return qndata.cryptofutures.load_data(tail=period, dims=("time","field","asset"))
      
      data = qndata.cryptofutures.load_data(min_date='2014-01-01')
      
      
      result = qnop.optimize_strategy(
          data,
          single_pass_strategy,
          qnop.full_range_args_generator(
              ma_slow_param=range(10, 150, 5), # min, max, step
              ma_fast_param=range(5, 100, 5)   # min, max, step
          ),
          workers=1 # you can set more workers on your PC
      )
      
      qnop.build_plot(result)
      
      print("---")
      print("Best iteration:")
      display(result['best_iteration'])
      
      A support 2 Replies Last reply Reply Quote 0
      • A
        antinomy @magenta.grimer last edited by

        @magenta-grimer Your strategy selects only the last MA values but you need them all to get full backtest results (single pass). By changing these lines

            ma_slow= qnta.lwma(close, ma_slow_param)#.isel(time=-1)   
            ma_fast= qnta.lwma(close, ma_fast_param)#.isel(time=-1)
        

        I get

        ---
        Best iteration:

        {'args': {'ma_slow_param': 125, 'ma_fast_param': 10},
        'result': {'equity': 149.87344883417938,
        'relative_return': -0.07047308274265918,
        'volatility': 0.6090118757480503,
        'underwater': -0.07047308274265918,
        'max_drawdown': -0.7355866115241121,
        'sharpe_ratio': 0.9776088443081092,
        'mean_return': 0.5953753960199653,
        'bias': -1.0,
        'instruments': 1.0,
        'avg_turnover': 0.06404024691932551,
        'avg_holding_time': 72.70270270270271},
        'weight': 0.9776088443081092,
        'exception': None}

        1 Reply Last reply Reply Quote 1
        • support
          support @magenta.grimer last edited by

          @magenta-grimer hi, answer by @antinomy is correct.

          Sorry for the confusion, we need a balance between fast execution (single pass) and safe one (multi pass).

          1 Reply Last reply Reply Quote 0
          • A
            antinomy last edited by

            There is a way to use the optimizer with a (stateful) mulit pass algo, but depending on the total number of changed parameters it can take a very long time. However, if it runs on a local computer with many workers this can still be useful.

            We could run the backtester with the multi pass algo to get all the weights for the test period and pass these weights to the optimizer.
            There's just one problem with this: you can't pass changed parameters to the strategy using the backtester.
            In order to solve this I created a nested function where the outer function takes the changed parameters from the optimizer. The inner function is the actual multi pass strategy and doesn't define the params but just uses the ones from the outer function. Still within the outer function we run the backtester with one set of params, get the weights it returns and return them to the optimizer.

            The time it takes to run the optimization would roughly be
            (time for 1 multi pass backtest) x (total number of parameter changes) / (number of workers that are able to run)
            So if one multi pass takes 1 minute, you want to optimize 10 parameter changes and can run 5 workers it would take about 2 minutes.

            Here's an example based on the one above with 2 parameter changes and 2 workers:

            import qnt.data as qndata
            import qnt.ta as qnta
            import qnt.optimizer as qnop
            import qnt.backtester as qnbt
            
            import xarray as xr
            
            
            def load_data(period):
                """Loads the BTC Futures data for the BTC Futures contest"""
                return qndata.cryptofutures.load_data(tail=period, dims=("time", "field", "asset"))
            
            
            def multi_pass_strategy(data, ma_slow_param=50, ma_fast_param=10):
                """The outer function gets called by the optimizer with changed params, the inner function gets passed
                to the backtester."""
            
                def strategy(data, state):
            
                    # The state isn't used in this example, this is just to show that it can be used while optimizing.
                    if state is None:
                        state = 0
                    state += 1
            
                    close = data.sel(field="close")
                    ma_slow = qnta.lwma(close, ma_slow_param).isel(time=-1)
                    ma_fast = qnta.lwma(close, ma_fast_param).isel(time=-1)
            
                    weights = xr.zeros_like(close.isel(time=-1))
                    weights[:] = 1 if ma_fast > ma_slow else -1
            
                    return weights, state
            
                """The backtester returns all weights for the test period which will then be returned to the optimizer"""
                weights, state = qnbt.backtest(
                                                strategy=strategy,
                                                competition_type="cryptofutures",
                                                load_data=load_data,
                                                lookback_period=700,
                                                start_date='2014-01-01',
                                                build_plots=False,
                                            )
                return weights
            
            
            data = qndata.cryptofutures.load_data(min_date='2014-01-01')
            
            result = qnop.optimize_strategy(
                data,
                multi_pass_strategy,
                qnop.full_range_args_generator(
                    ma_slow_param=range(50, 60, 5),  # min, max, step
                    # ma_fast_param=range(5, 100, 5)  # min, max, step
                ),
                workers=2  # you can set more workers on your PC
            )
            
            print("---")
            print("Best iteration:")
            print(result['best_iteration'])
            
            qnop.build_plot(result)
            
            

            There might be more efficient ways to do this, so if anyone has one feel free to post it here.

            1 Reply Last reply Reply Quote 0
            • 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