Skip to main content

Using AutoConfig to add and remove pairs

This guide will walk you through a comprehensive workflow for adding, evaluating, and removing trading pairs using AutoConfig.

Before You Start​

  • Ensure AutoConfig is enabled in your Gunbot setup.
  • Adjust the given settings according to your requirements and save them in the autoconfig.json file. While our visual editor can technically create these jobs, using a code editor is usually simpler. If you plan to use AutoConfig frequently, becoming proficient in JSON syntax is advisable.
  • Your Gunbot setup should already include a stepgrid strategy for the addPairs job to function correctly. A stepgrid strategy set up. If absent, add a pair with default stepgrid settings via the Gunbot interface, save changes, then remove the pair.

The Plan​

To build a self-optimizing system for pair selection and rotation, we'll set up four AutoConfig jobs, each with a distinct role:

  1. A job that scans the markets for volatile pairs and regularly adds new trading pairs if new 'slots' become free.
  2. A job that gathers stats about realized and unrealized profits for each pair and stores them in a way that another AutoConfig job can use the data for identifying the worst-performing pair.
  3. A job that marks a pair for deletion because it performs the worst.
  4. A job that removes the worst-performing pair.

Step 1: Rule-based Adding of Trading Pairs​

The following job example tries to identify volatile pairs with acceptable spread and trading volume. Pairs are added with the stepgrid strategy, which is a fairly simple grid-like strategy with automatic targets and built-in price trailing.

Breakdown of Key Filters​

  • stdv: The minStandardDevPctInterval filter ensures that the pair has a standard deviation (expressed as a percentage of the price) of at least 0.3% during the last hour. This helps identify volatile pairs.

  • price: The minPricePctChangeInterval filter checks that the price is currently at least 1% higher than the average price in the last hour. This helps find pairs with significant price movements.

  • spread: The maxSpreadPct filter ensures that the bid and ask have a spread of at most 0.2%. This helps select pairs with acceptable market liquidity.

  • volume: The aboveMedianVolume filter checks that the pair has an above-median trading volume compared to all other USDT pairs on the exchange. This helps identify pairs with sufficient market activity.

These ticker filters work together to identify trading pairs that meet the desired criteria for volatility, price movement, spread, and trading volume. By applying these filters, the addPairs job can automatically add new, potentially profitable pairs to the trading strategy.

The goal of this job is to regularly scan the markets, identify suitable pairs, and add them to the trading configuration, up to a maximum of 5 pairs. This helps create a dynamic and self-optimizing trading system that adapts to market conditions.

{
"addPairs_binance": {
"pairs": {
"exclude": "",
"include": "USDT-",
"maxPairs": "5",
"noCrossOver": true,
"exchange": "binance"
},
"filters": {
"stdv": {
"type": "minStandardDevPctInterval",
"target": "0.3"
},
"price": {
"type": "minPricePctChangeInterval",
"target": "1"
},
"spread": {
"type": "maxSpreadPct",
"max": "0.2"
},
"volume": {
"type": "aboveMedianVolume"
}
},
"overrides": {
"TRADING_LIMIT": 50,
"BUY_METHOD": "stepgrid",
"SELL_METHOD": "stepgrid",
"MAX_BUY_COUNT": "40",
"MIN_VOLUME_TO_SELL": 0.05,
"MAX_INVESTMENT": "2000",
"PERIOD": 15,
"AUTO_STEP_SIZE": true,
"STEP_SIZE": "500",
"ENFORCE_STEP": false,
"unit_cost": true,
"STOP_AFTER_SELL": false,
"FOREVER_BAGS": false,
"BUY_ENABLED": true,
"SELL_ENABLED": true,
"PROTECT_PARTIAL_SELL": true,
"SMAPERIOD": "50",
"ATR_PERIOD": "50",
"KEEP_QUOTE": "0",
"IGNORE_TRADES_BEFORE": "0"
},
"schedule": "*/5 * * * *",
"type": "addPairs",
"strategy": "stepgrid",
"snapshots": "12",
"setITB": false,
"resume": true,
"debug": false,
"muteTG": true,
"enabled": true
}
}

Breakdown of Key Components​

  • pairs: Targets all pairs on Binance by including those with a "-" sign.
  • filters: Define the properties that potential new pairs must have to get added, such as minimum standard deviation, price change, spread, and trading volume.
  • overrides: Sets strategy parameters for newly added pairs, in this case, the default list of stepgrid parameters.
  • schedule: The job runs every 5 minutes, as specified by the cron notation */5 * * * *.
  • snapshots: The last 12 ticker snapshots are kept internally, so that we have a rolling data cache for the timespan of one hour, used for filtering.
  • type: The addPairs job type is suitable for adding new trading pairs.

Step 2: Storing Performance Data​

To keep track of the worst-performing trading pair and eventually replace it, we need to store some key performance data. This is done using a job that stores the data to 'pairVariables', a memory location shared between all AutoConfig jobs.

{
"storePerformanceData_binance": {
"pairs": {
"exclude": "",
"include": "USDT-",
"exchange": "binance"
},
"filters": {
"alwaysPasses": {
"type": "custom",
"target": " true"
}
},
"setPairVariable": {
"realizedPnl": " this.pair.orders.filter(order => order.time > (this.pair.whatstrat.timestamp * 1000)).reduce((acc, order) => acc + order.pnl, 0)",
"unrealizedPnl": " ((this.pair?.quoteBalance * this.pair?.Bid) - (this.pair?.quoteBalance * this.pair?.ABP)) || 0"
},
"overrides": {},
"clearOverrides": false,
"schedule": "* * * * *",
"type": "manageOverrides",
"debug": false,
"enabled": true
}
}

Breakdown of Key Components​

  • pairs: Targets all pairs on Binance by including those with a "-" sign.
  • filters: Only has a single custom filter that always passes, so that pairVariables are set every time the job runs regardless of filters.
  • overrides: This job sets no overrides.
  • schedule: The job runs every minute, as specified by the cron notation * * * * *.
  • type: The manageOverrides job type is used to process some logic without direct effects on trading behavior or configuration.
  • setPairVariable: Stores data, such as realized and unrealized profits, to a location that can be accessed by all AutoConfig jobs.

Explanation of the custom code​

  • The realizedPnl code calculates the realized profit and loss for the pair by filtering the orders to only include those made after the pair was last added (using the this.pair.whatstrat.timestamp value), and then summing up the pnl (profit and loss) values of those orders.
  • The unrealizedPnl code calculates the unrealized profit and loss for the pair by taking the current quote balance, multiplying it by the current bid price, and subtracting the average buy price multiplied by the quote balance. This gives the current unrealized profit or loss.

This ensures that the pair with the worst overall performance (lowest combined realized and unrealized profit) is identified and marked for removal in the next step.

These values are then stored in AutoConfig pairVariables, which can be accessed by all AutoConfig jobs.

Step 3: Marking the Worst Performing Pair​

Using the data collected in the previous step, this job sets the STOP_AFTER_SELL pair setting to false for the worst-performing pair. This ensures that the pair continues trading, but only until it manages to perform a full sell.

{
"identifyWorstPerformer_binance": {
"pairs": {
"exclude": "",
"include": "USDT-",
"exchange": "binance"
},
"filters": {
"Sanity checks": {
"type": "custom",
"target": " this.pairVariables"
}
},
"overrides": {
"STOP_AFTER_SELL": " Object.entries(this.pairVariables.binance).filter(([_, v]) => v.realizedPnl !== undefined && v.unrealizedPnl !== undefined && v.timestamp !== undefined).sort(([_, a], [__, b]) => (a.realizedPnl + a.unrealizedPnl) - (b.realizedPnl + b.unrealizedPnl))[0][0] === this.pairName"
},
"clearOverrides": false,
"schedule": "* * * * *",
"type": "manageOverrides",
"debug": false,
"enabled": true
}
}

Breakdown of Key Components​

  • pairs: Targets all pairs on Binance by including those with a "-" sign.
  • filters: Only has a single custom filter that checks if there is a pairVariables object to work with.
  • overrides: This job sets one override with a dynamic value. Only one of the pairs at a time can get a value of 'true'.
  • schedule: The job runs every minute, as specified by the cron notation * * * * *.
  • type: The manageOverrides job type is commonly used to analyze internal bot data and change the assigned settings (also called overrides) for a trading pair.

Explanation of the custom code​

  • The code first filters the pairVariables.binance object to only include pairs that have valid realizedPnl, unrealizedPnl, and timestamp values.
  • It then sorts the filtered pairs based on the sum of their realizedPnl and unrealizedPnl values, in ascending order (i.e., the pair with the lowest total profit/loss is first).
  • Finally, it checks if the name of the current pair (this.pairName) matches the name of the pair with the lowest total profit/loss (the first element in the sorted array). If so, it sets the STOP_AFTER_SELL override to true for that pair, which will mark it for deletion.

This ensures that the pair with the worst overall performance (lowest combined realized and unrealized profit) is identified and marked for removal in the next step.

Step 4: Removing the Worst Performing Pair​

The final job removes the marked pair, ensuring the trading setup remains optimized by only keeping the most profitable or promising pairs.

{
"removePairs_binance": {
"pairs": {
"exclude": "",
"noBag": true,
"removeDisabled": false,
"notRemoveBefore": 720,
"exchange": "binance",
"include": "USDT-"
},
"filters": {
"status": {
"type": "custom",
"target": " this.pair.whatstrat.STOP_AFTER_SELL === true"
}
},
"schedule": "* * * * *",
"type": "removePairs2",
"debug": false,
"muteTG": true,
"enabled": true
}
}

Breakdown of Key Components​

  • pairs: Targets all pairs on Binance by including those with a "-" sign. The noBag: true property ensures that only pairs that have completely sold their assets are eligible for removal. The notRemoveBefore value of 720 means that it only considers pairs for removal, after they have traded for 12h (720m).
  • filters: Only has a single custom filter that checks if the pair has its STOP_AFTER_SELL override set to true, our signal for deletion.
  • schedule: The job runs every minute, as specified by the cron notation * * * * *.
  • type: The removePairs2 job type is used for removing trading pairs without additional API overhead, it only uses data already collected by Gunbot core.

Conclusion​

This guide outlines a basic strategy for managing trading pairs completely automatically in Gunbot using AutoConfig.

The steps provided serve as a foundation for creating a dynamic and self-optimizing trading system, customizable according to individual trading needs and preferences. By understanding the technical details and the overall workflow, you can further enhance and refine the system to suit your specific trading requirements.

There are definitely more comprehensive ways to evaluate which trading pairs to add or which ones to keep, this guide just gave an example of what you can do. Your bot, your rules!

Full autoconfig.json​

You can copy the full combined autoconfig setup below.

{
"addPairs_binance": {
"pairs": {
"exclude": "",
"include": "USDT-",
"maxPairs": "5",
"noCrossOver": true,
"exchange": "binance"
},
"filters": {
"stdv": {
"type": "minStandardDevPctInterval",
"target": "0.3"
},
"price": {
"type": "minPricePctChangeInterval",
"target": "1"
},
"spread": {
"type": "maxSpreadPct",
"max": "0.2"
},
"volume": {
"type": "aboveMedianVolume"
}
},
"overrides": {
"TRADING_LIMIT": 50,
"BUY_METHOD": "stepgrid",
"SELL_METHOD": "stepgrid",
"MAX_BUY_COUNT": "40",
"MIN_VOLUME_TO_SELL": 0.05,
"MAX_INVESTMENT": "2000",
"PERIOD": 15,
"AUTO_STEP_SIZE": true,
"STEP_SIZE": "500",
"ENFORCE_STEP": false,
"unit_cost": true,
"STOP_AFTER_SELL": false,
"FOREVER_BAGS": false,
"BUY_ENABLED": true,
"SELL_ENABLED": true,
"PROTECT_PARTIAL_SELL": true,
"SMAPERIOD": "50",
"ATR_PERIOD": "50",
"KEEP_QUOTE": "0",
"IGNORE_TRADES_BEFORE": "0"
},
"schedule": "*/5 * * * *",
"type": "addPairs",
"strategy": "stepgrid",
"snapshots": "12",
"setITB": false,
"resume": true,
"debug": false,
"muteTG": true,
"enabled": true
},
"storePerformanceData_binance": {
"pairs": {
"exclude": "",
"include": "USDT-",
"exchange": "binance"
},
"filters": {
"alwaysPasses": {
"type": "custom",
"target": " true"
}
},
"setPairVariable": {
"realizedPnl": " this.pair.orders.filter(order => order.time > (this.pair.whatstrat.timestamp * 1000)).reduce((acc, order) => acc + order.pnl, 0)",
"unrealizedPnl": " ((this.pair?.quoteBalance * this.pair?.Bid) - (this.pair?.quoteBalance * this.pair?.ABP)) || 0"
},
"overrides": {},
"clearOverrides": false,
"schedule": "* * * * *",
"type": "manageOverrides",
"debug": false,
"enabled": true
},
"identifyWorstPerformer_binance": {
"pairs": {
"exclude": "",
"include": "USDT-",
"exchange": "binance"
},
"filters": {
"Sanity checks": {
"type": "custom",
"target": " this.pairVariables"
}
},
"overrides": {
"STOP_AFTER_SELL": " Object.entries(this.pairVariables.binance).filter(([_, v]) => v.realizedPnl !== undefined && v.unrealizedPnl !== undefined && v.timestamp !== undefined).sort(([_, a], [__, b]) => (a.realizedPnl + a.unrealizedPnl) - (b.realizedPnl + b.unrealizedPnl))[0][0] === this.pairName"
},
"clearOverrides": false,
"schedule": "* * * * *",
"type": "manageOverrides",
"debug": false,
"enabled": true
},
"removePairs_binance": {
"pairs": {
"exclude": "",
"noBag": true,
"removeDisabled": false,
"notRemoveBefore": 720,
"exchange": "binance",
"include": "USDT-"
},
"filters": {
"status": {
"type": "custom",
"target": " this.pair.whatstrat.STOP_AFTER_SELL === true"
}
},
"schedule": "* * * * *",
"type": "removePairs2",
"debug": false,
"muteTG": true,
"enabled": true
}
}