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 three files, namely requirements.txt
, exchange-keys.json
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
exchange-keys.json
This is a required file that should be placed in the directory where you run the strategy, containing the API key and secrets for your preferred exchange.
Kindly refer to the documentation of your preferred exchange on how to acquire these keys.
The exchange-keys.json schema can be seen with the example below:
[
{
"exchange": "{EXCHANGE}",
"api_key": "{YOUR_EXCHANGE_API_KEY}",
"api_secret": "{YOUR_EXCHANGE_API_SECRET}",
"api_passphrase": "{YOUR_EXCHANGE_API_PASSPHRASE}",
"environment": "{EXCHANGE_ENVIRONMENT}"
}
]
As of right now the only supported exchange value is bybit_linear
.
The table below shows the current supported exchanges.
Exchange | Supported |
---|---|
bitget_linear | ✅ |
bitget_spot | ❌ |
bybit_linear | ✅ |
bybit_spot | ❌ |
binance_linear | ✅ |
binance_spot | ❌ |
okx_linear | ✅ |
okx_spot | ❌ |
The exchange environment denoted here refers to the either mainnet
or testnet
.
An example of this exchange-keys.json would be
[
{
"exchange": "bybit_linear",
"api_key": "my_bybit_api_key",
"api_secret": "my_bybit_api_secret",
"environment": "testnet"
}
]
Do note that some exchanges (such as OKX and Bitget that are currently supported) has a 'Passphrase' for their api-keys, this api_passphrase is optional and is only required when passing credentials for exchanges mentioned prior.
[
{
"exchange": "bitget_linear",
"api_key": "my_bitget_api_key",
"api_secret": "my_bitget_api_secret",
"api_passphrase": "my_bitget_api_passphrase"
"environment": "mainnet"
}
]
For an example if your current working directory is strategies
the file should be placed as such:
- requirements.txt
- exchange-keys.json
- main.py
if your current working directory is before strategies
then place the exchange-keys.json as such:
- requirements.txt
- exchange-keys.json
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.LiveTestnet,
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,
exchange_keys="path/to/your/exchange-keys.json",
)
This is the simplest possible RuntimeConfig
setting.
- The
mode
parameter value is theRuntimeMode
enum provided by cybotrade and has two possible values,Live
andLiveTestnet
. - 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. - The
exchange_keys
parameter value is the file path to your exchange-keys.json. Note that the path is based off your current working directory when you run themain.py
script.
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())
Great job reaching here!
Backtesting
As of writing, the latest Cybotrade no longer actively maintains Backtesting mode. We suggest performing backtesting with alternative methods to ensure accuracy.
Deprecated Documentation
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.
LiveTestnet
Execute your strategy on Testnet with RuntimeMode.LiveTestnet
, to filter for any unexpected scenarios when running your automated strategy live!
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 approach is to go through these additional documentations: