This is a simple trading bot made with the following goals in mind:
- Easy to create a strategy. With a regular js object and a handful of API methods, you can get market data, wallet balances, and perform swaps.
- Easy to extend. You can add new exchanges and dexes on new modules, increasing the bot's reach.
- Full package. You can scan data from exchanges, run backtests, live or paper trade, and monitor strategy performance on the web.
I created this bot for personal use, and there may have bugs in it. So if you like this code and decide to use it, you are accepting the risk. If you lose money because of any bug, that is on you.
I provide this code "as-is" with no guarantee of support of any kind. If you find any issue in the code, you may open a new issue on this repo, and I will check it on my own time.
Clone this repo
git clone https://github.com/werlang/trader-bot.git
Install dependencies
npm i
Create the SQL database (needed to save the data fetched from exchange):
If you do not have a SQL server installed on your machine, go install it first
mysql -u <DB_USER> -p
mysql> create database <DB_NAME>;
mysql> use <DB_NAME>;
mysql> source asset/database.sql;
Rename (or copy then rename) .env.example file to .env. Then fill its contents according to your db configuration:
DB_USER="root"
DB_PORT=3306
DB_HOST="localhost"
DB_DATABASE="trader_db"
DB_PASSWORD="root_password"
WEBSERVER_PORT=3000
Rename (or copy then rename) config.json.example file to config.json. Then fill its contents according to your db configuration:
{
"fromTime": "2021-01-01 00:00:00.000",
"toTime": "2021-12-21 00:00:00.000",
"exchange": {
"name": "binance",
"apiKey": "YOUR_API_KEY",
"symbol": "ETHUSDT"
},
"dex": {
"network": "ethereum",
"name": "paraswap",
"asset": "ETH",
"currency": "USDT",
"wallet": "YOUR_WALLET_ADDRESS",
"private": "YOUR_WALLET_PRIVATE_KEY"
},
"telegram": {
"enabled": false,
"token": "TELEGRAM_BOT_TOKEN",
"chatId": "CHAT_ID"
},
"timeframe": 60,
"strategy": "dca",
"verbose": 2,
"startingBalance": 1000,
"swapFee": 0.002,
"historySize": 720
}Now you can run the bot using one of the modes:
- Scanner:
npm run scanner - Backtest:
npm run backtest - Paper trade:
npm run paper - Live trade:
npm start
Here is the descriptions of every field:
fromTime: Time you will start to scan for candles. Used for scanner and backtest modes.toTime: Time your candle scanning will end. Used for scanner and backtest modes.exchange.name: Name of module under theexchangefolder for interacting with exchange.exchange.apiKey: API key on the exchange.exchange.symbol: Which pair should be scanned on the exchange.dex.network: Chain's network name.dex.name: Name of module under thedexfolder for interacting with the dex platform.dex.asset: Token name for the asset you are wanting to trade.dex.currency: Token name for the currency you are wanting to trade the asset for.dex.wallet: Your wallet address that will interact with the blockchain.dex.private: Your wallet's private key.telegram.enabled: Enable or disable Telegram alerts for swaps.telegram.token: Telegram bot's token that will send alerts.telegram.chatId: Telegram chat id of the group/user the bot will send alerts to.timeframe: The amount of minutes for each candle. The time frame for your trades.strategy: Name of your file under thestrategiesfolder containing your strategy.verbose: Amount of details you are willing the console to show (0-2);startingBalance: The amount of currency your wallet will have at start. Used for backtest and paper trade only.swapFee: The percentage of the swapped value that will be paid as platform fee at every swap. Used for backtest and paper trade only.historySize: Amount of candles that will be loaded as historical data before your strategy start to run. Used for paper and live trade only.
There are four main modes: scanner, backtest, paper trade and live trade.
npm run scanner
This mode is used to fetch candle data information from the exchange. First you will need to change config.json's fromTime and toTime field.
The scanner module will check the database, and for all candles missing in the given interval, it will use your exchange API key to fetch data from the exchange, then save the candle on the database.
npm run backtest
This mode uses the same datetime interval as the scanner module. But this time it simulates trades on that interval.
It loads your strategy strategy field from config.json.
Before running anything, it will call the init() method form your strategy.
You will receive a custom amount of fake curerency in your wallet (determined by the startingBalance field in the config) for this simulation.
After that, at each candle interval, defined by the timeframe field form config, it will call the update() method from your strategy. The update method received a candle argument, that can be used inside your strategy.
Since this is a simulations, any swap performed by the strategy in this mode will affect only the fake currency and asset present in your wallet.
Check the dca.js strategy for a general idea about how strategies should be built.
If at any time, the needed candle is not available on memory, it will be fetched from the database. If it is not available on the database, it will be fetched from the exchange.
After the backtest is run, you will see a report of your trades on the console:
--- TRADING SUMMARY ---
Starting time: 2022-05-06T00:00:00.000Z
Ending time: 2022-09-05T23:59:59.999Z
Period: 122 days
Starting price: $2737.67
Ending price: $1617.80
Profit if HODLing: -40.9060%
Starting balance: $1000.00
Ending balance: $1046.35
Strategy profit: 4.6349%
Num. Swaps: 6
Fee paid: $2.18
APR: 0.0368%
APY: 14.3905%
Sharpe ratio: 0.0024
Max. Drawdown: 0.2374
While on backtest, paper or live trade, the bot will launch by default a web server, allowing you to check in real time how your strategy is doing.
Check the port used by the webserver in the .env file, go to a web browser, and type:
http://localhost:PORT
npm run paper
On this mode, market watching will be made from live data, but you will only trade with fake currency and asset, like in the backtest mode.
Before anything, on this mode the bot will fetch some data from database (or exchange, in case it does not have available data) to compose the pre-trade data. The amount of candles is determined by the historySize on the config.
After that, the strategy will be started and run in the same way as in the backtest mode.
Since this mode will deal with live data, at each minute interval the bot will fetch a new candle from exchange.
Unlike the backtest, you can check the web server page to monitor your strategy while the strategy is still running. Just remember to refresh the page to update the data.
npm start
On this mode, just like on paper trade, you will trade on live data. But on live trade you will deal with real money.
Your wallet will be loaded on the bot using the dex.wallet and dex.private fields from config.
Every swap performed by your strategy will call the dex module and send the transaction to the blockchain.
BEWARE: You can lose your fund while using live trade mode. Use this at your own risk.
To write your strategy, you will need to create a file under the strategies folder, then put its name on the strategy field on the config.
This strategy needs to export an object with at least two methods: init and update:
module.exports = {
init: async function() {
// this is executed once, before start
},
update: async function(candle) {
// this is executed at every time step
// candle can be used to get current market data
}
}The argument candle from the update method is an object, containing current market data. It has the following fields:
tsopen: (Date): Starting time of this candle.tsclose: (Date): Ending time of this candle.open: (Number): Asset price when the candle started.close: (Number): Asset price when the candle ended.low: (Number): Lowest asset price seen on the entire candle timeframe.high: (Number): Highest asset price seen on the entire candle timeframe.volume: (Number): Volume of asset traded during this candle timeframe.samples: (Number): Number of lower timeframe candles composing this entire candle.
Example:
{
tsopen: "2022-05-08T03:00:00.000Z",
tsclose: "2022-05-08T03:59:59.999Z",
open: 2549.51,
close: 2545.08,
low: 2517,
high: 2553.29,
volume: 67955.88060000002,
samples: 76402
}You can also create a .json file with the same name as your strategy file. The bot will automatically import it and expose its contents on this.settings.FIELD:
myStrategy.json
{
"myField": "value"
}myStrategy.js
strategy.update: async function(candle) {
const field = this.settings.myField;
console.log(field); // will print 'value'
}There are a handful of methods you need to know about to create your own strategy:
Returns an object representing the amount of currency and asset in the wallet
{
asset: Number,
currency: Number
}Same as getWallet, but returns a single number, representing the sum of currency and asset, converted to currency.
Return an array of candles between fromTime and toTime. Both arguments can be positive or negative.
- Values from 0...N represent the desired nth candle since the strategy started.
- Values from -N...-1 represent the desired nth candle, decresing. (-1 is the last candle).
fromTimecan also be'start': This is the same as 0.toTimecan also be'end'orundefined: This is the same as -1.
Overrides the candle object received on the update method. This is very useful when you want to set custom values for the candle object at each step of your strategy (like setting custom indicator values for each candle), without needing to set it inside your update method.
The callback argument is a function with the following format:
function callback(candle) {
// your logic here
return candle;
}At each time step, your callback function will be called, and the returned candle will be replaced by the default candle value. Then you can use this custom candle inside your strategy.
Check this example:
strategy.init = async function() {
this.setHistory(candle => {
candle.myField = myMethod();
return candle;
});
};
strategy.update = async function(candle) {
// candle will have all regular fields, plus a myField value
// that will receive the return of myMethod, called at each step.
};Make the swap. If on live trade mode, the bot will call the dex and web3 to write the tx on the blockchain
amountis a positive number indicating the amount of tokens you are willing to sell.currency == truemeans that you are willing to sell the currency for asset.currency == falsemeans that you are willing to sell the asset for currency.
Alias for swap(amount, true).
Alias for swap(amount, false).
Check the dca.js file to get a feeling about how to build a strategy.
Return the current time of the simulation. format argument can be either:
index: The method will return the number of candles since the start of the process. 0 == first candle.timestamp: The method will return the current candle timestamp.date: The method will return the Date object of the current candle.undefined: The method will return an object with all the following format.
{
index: Number,
timestamp: Number,
date: Date
}This method can be used to add custom indicators on the web chart. To use this first you must add a custom field on the candle object using the setHistory method.
name: The field name you added to thecandleobject using thesetHistorymethod.color: A string representing the color of the indicator line to be added to the chart. If the indicator is an object with several fields, you must send an array of colors.
Example:
this.SMA = new this.indicators.SMA({period: 60, values: []});
this.addIndicatorView('SMA', '#ffffff');
this.setHistory(candle => {
candle.SMA = this.SMA.nextValue(candle.close);
return candle;
});Since most stretegies need indicators to do their magic, this bot comes with technicalindicators module pre-installed. It can be accessed with this.indicators:
const SMA = this.indicators.SMA;
const prices = [1,2,3,4,5,6,7,8,9,10,12,13,15];
const period = 10;
SMA({ period: period, values: prices });Go to the module's documentation to learn how to use each indicator.
You can also install any other node modules that you think it might help you improving your strategy (e.g. node-fetch):
npm i node-fetch
Then you can use normally on your strategy:
const fetch = require('node-fetch');
...
const req = await fetch('example.com/api-endpoint');
const data = await req.json();
...The bot can send you alerts whenever it performs a swap. If you are willing to receive those alerts, you should set the telegram.enabled field to true.
You also need to have a Telegram bot. If you don't have one, just go to their reference page and learn how to create one.
After that, you need to fill telegram.token and telegram.chainId fields in the config file.
Done! From now on you will receive a Telegram message whenever your strategy perform a swap.
If you like this bot, and think you can contribute with its code, just create a pull request. I would love to integrate other dexes and exchanges on it. It would be very nice to include several community created sample strategies also.
I also accept donations from any EVM compatible network (Ethereum, Polygon, BNB chain, Avalanche, Fantom, Arbitrum, Optimism, and so on).

