Python
Overview

Overview

Starting your Strategy

In order to create a strategy on cybotrade you only have to understand two things:

  1. A set of handlers for market events
  2. How to interact with the Cybotrade runtime

Let's take a deeper look on how to do them in code.

File Structure

You need to have two files namely requirements.txt and main.py.

    • requirements.txt
    • exchange-keys.json
    • main.py
  • requirements.txt

    This is the file where you include the dependencies of your Python script, which means you can use any external libraries as long as they are published on PyPi eg. TA-lib, numpy, pandas, etc. An example is shown as follows

    cybotrade>=1.4.20
    colorlog>=6.7.0
    TA-Lib>=0.4.26
    pandas>=2.0.2
    matplotlib>=3.7.1

    main.py

    This is the file where you write your Python code, the most important thing is that you must

    • Define your own 'strategy' class that extends the Strategy class from Cybotrade.
    from cybotrade.strategy import Strategy
     
    class MyStrategy(Strategy):
      pass

    Your 'strategy' class (MyStrategy) will be the main way to interact with the Cybotrade runtime by defining and overriding the handlers provided by the Strategy class from cybotrade.

    Strategy Implementation

    Handlers for market events

    The runtime subscribes market data stream such as candle, public trades etc. from exchanges and it is up to strategy developers to implement the handlers taken for these events. It can be done by overriding the Strategy class' methods.

    The most basic event handler is on_candle_closed, which is a handler for when a candle closes.

    from cybotrade.strategy import Strategy
     
    class MyStrategy(Strategy):
      # This method will be invoked every time a candle closes, if the strategy
      # subscribed to a 1 hour candle then this method will be invoked every hour.
      async def on_candle_closed(self, strategy, topic, symbol):
        # Here you can define the actions to be taken such as calculating indicators when
        # a candle has closed.
        print("A Candle has just closed!")
        ...

    There are also event handlers like on_order_update as well as on_datasource_interval that would be commonly used.

    from cybotrade.strategy import Strategy
     
    class MyStrategy(Strategy):
        async def on_candle_closed(self, strategy, topic, symbol):
            print("A Candle has just closed!")
            ...
     
        async def on_datasource_interval(self, strategy, topic, data_list):
            print("CryptoQuant open-interest data!")
     
        async def on_order_update(self, strategy, udpate):
            print("Take Profit!")
     

    There are a few additional event handlers provided by the Strategy class that can be overriden that is documented here.

    StrategyTrader

    The main way to interact with the Cybotrade runtime, in order to perform actions on the exchange of your choice, is to use the strategy parameter from the handler function. This parameter has a type of StrategyTrader.

    from cybotrade.strategy import Strategy
     
    class MyStrategy(Strategy):
        async def on_candle_closed(self, strategy, topic, symbol):
            # Retrieved available balance from exchange.
            bal = await strategy.get_current_available_balance(
                exchange=Exchange.BybitLinear,
                symbol=symbol
            )
            # Print available balance 
            print(bal)

    The StrategyTrader is also how you are able to place orders:

    from cybotrade.strategy import Strategy
    from cybotrade.models import OrderSide, Exchange
     
    class MyStrategy(Strategy):
        async def on_candle_closed(self, strategy, topic, symbol):
            # Open a Buy order.
            await strategy.open(
                side=OrderSide.Buy,
                exchange=Exchange.BybitLinear,
                symbol=symbol,
                quantity=0.001,
            )
            print(f"Placed order for 0.001 of {symbol}")

    There are many APIs exposed by the StrategyTrader that is documented here.

    RuntimeConfig

    The RuntimeConfig struct provided by cybotrade is the self-explanatory it is the config settings for the Cybotrade Runtime.

    Data selection, such as candle data, as well as Cybotrade Datasource data will be defined here:

    from cybotrade.models RuntimeConfig, RuntimeMode
    from datetime import datetime, timezone
    ...
     
    config = RuntimeConfig(
        mode=RuntimeMode.Backtest,
        datasource_topics=[],
        candle_topics=["candles-1d-BTC/USDT-bybit"],
        active_order_interval=1,
        start_time=datetime(2020, 4, 1, 0, 0, 0, tzinfo=timezone.utc),
        end_time=datetime(2024, 1, 1, 0, 0, 0, tzinfo=timezone.utc),
        data_count=100,
    )

    This is the simplest possible RuntimeConfig setting.

    • The mode parameter value is the RuntimeMode enum provided by cybotrade and has three possible values, Live, LiveTestnet and Backtest.
    • The datasource_topics parameter value is an array of strings that accepts datasource topics to retrieve Cybotrade Datasource data, more on this later.
    • The candle_topics parameter value is an array of strings, candle data is provided for free and can be referred to here.
    • The active_order_interval parameter is only relevant in RuntimeMode.Live and RuntimeMode.LiveTestnet, so this can be ignored for now.
    • The start_time and end_time parameters is the timeframe in which you would like to run your backtest. In this example it would be backtesting a strategy logic from 01-04-2020 to 01-01-2024.
    • The data_count parameter is not necessary to be understood for this guide, but it will be helpful to take a look at this documentation after running the guide.

    There is an option for RuntimeConfig which is api_key and api_secret, this value is not required when requesting for candle data.

    There are a few additional options available for the RuntimeConfig that is described here.

    Permutation and Hyper Parameter Tuning

    Permutation

    As of the time of writing this, the current Cybotrade can only start the runtime through the Permutation class. This will be changed in the future.

    Currently in order to actually start and run the strategy, you will have to wrap the RuntimeConfig in the Permutation class provided by cybotrade.

    from cybotrade.permutation import Permutation
    ...
    permutation = Permutation(config)

    The Permutation class allows the user to run the same strategy multiple times with different parameters through Hyper Parameter Tuning.

    Hyper Parameters

    At face value, the Hyper Parameters is just a dictionary:

    hyper_parameters = {}

    Temporarily, for this example, we can leave the hyper_parameters empty.
    The use case for Permutations and Hyper Parameter Tuning is explored further here.

    Running the Strategy

    As of the time of writing, this is the only way to start a strategy, this will be updated in the future.

    In order to start the strategy, you will have to define an async function, this async function will then be passed into asyncio.run().

    If you're unsure as to what asyncio is, there is no need to worry. You can imagine it as an engine to run asynchronous functions upon. However, the only time it is used is in the starting of the strategy and nowhere else.

    In the async function, you will have to call upon the .run() method of the Permutation class passing in your overidden Strategy class (MyStrategy) as well as the hyper_parameters:

    import asyncio
    ...
    async def start_strategy():
        await permutation.run(hyper_parameters, MyStrategy)
     
    asyncio.run(start_strategy())

    Backtesting

    Great job reaching here!

    You are all set and ready to run a simple backtest that places an order everytime a candle closes by combining all the logic above into one simple python file:

    from cybotrade.strategy import Strategy
    from cybotrade.models import OrderSide, Exchange, RuntimeConfig, RuntimeMode
    from datetime import datetime, timezone
    from cybotrade.permutation import Permutation
    import asyncio
     
    class MyStrategy(Strategy):
        async def on_candle_closed(self, strategy, topic, symbol):
            # Open a Buy order.
            await strategy.open(
                side=OrderSide.Buy,
                exchange=Exchange.BybitLinear,
                symbol=symbol,
                quantity=0.001,
            )
            print(f"Placed order for 0.001 of {symbol}")
     
    config = RuntimeConfig(
        mode=RuntimeMode.Backtest,
        datasource_topics=[],
        candle_topics=["candles-1d-BTC/USDT-bybit"],
        active_order_interval=1,
        start_time=datetime(2020, 4, 1, 0, 0, 0, tzinfo=timezone.utc),
        end_time=datetime(2024, 1, 1, 0, 0, 0, tzinfo=timezone.utc),
        data_count=100,
    )
     
    permutation = Permutation(config)
    hyper_parameters = {}
     
    async def start_runtime():
        await permutation.run(hyper_parameters, MyStrategy)
     
    asyncio.run(start_runtime())

    You should be able to run the following command to start your strategy:

    python main.py

    You will then be greeted with logs that should look similar to this: output of example strategy

    Congratulations! You just ran a backtest for a strategy!

    Backtest Performance

    Now you may notice that if you run the command

    ls

    you should be able to see a new json file generated like such: performance json in ls

    This performance_*.json can be plugged into our comprehensive Cybotrade Analysis (opens in a new tab) platform to perform performance analysis on your strategy!

    Moving Forward

    It is recommended to go through the following sequence to ensure your strategy is robust.

    Backtest

    Backtest your strategy and perform comprehensive and robust performance analysis through our Cybotrade Analysis (opens in a new tab) to ensure you always hit your Take Profits!

    LiveTestnet

    Run your strategy on Testnet with RuntimeMode.LiveTestnet, to filter for any unexpected scenarios when running your automated strategy!

    Live on Cybotrade Cloud

    Run your strategy without worries 24/7 on our Cybotrade Cloud (opens in a new tab) platform and start hitting your Take Profits!

    As simple as this example has made using cybotrade look, it is still much more flexible and powerful than what is presented here.

    There are more guides following this simple overview, that will extend upon what was used in this guide.

    A recommendation to start your aproach is to go through these additional documentations: