Overview
Starting your Strategy
In order to create a strategy on cybotrade you only have to understand two things:
- A set of handlers for market events
- 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!")
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 theRuntimeMode
enum provided by cybotrade and has three possible values,Live
,LiveTestnet
andBacktest
. - 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 inRuntimeMode.Live
andRuntimeMode.LiveTestnet
, so this can be ignored for now. - The
start_time
andend_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:
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:
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: