Skip to content
This repository has been archived by the owner on Feb 1, 2024. It is now read-only.

Commit

Permalink
New Trading Strategy: Pendulum Strategy (closes #427) (#428)
Browse files Browse the repository at this point in the history
* 1 - swing strategy

* 2 - add comments to sample_swing explaining config fields

* 3 - use correct cursor in sdex.go#GetTradeHistory

* 4 - swingLevelProvider should return cursor from result so it skips any noop trade items

noop trade items can be those that are irrelevant to the current bot instance

* 5 - convert IsCcxtTradeHistoryHack into incrementTimestampCursor on swingLevelProvider

* 6 - adjust sample spread amounts in sample_swing.cfg

* 7 - add BULL and ETHBULL assets

* 8 - better logging for price2LastPrice map and price fetched from map

* 9 - use orderConstraints in swingLevelProvider

* 10 - use orderConstraints from exchangeShim instead of sdex

* 11 - fixed getLastPriceFromMap and added TestGetLastPriceFromMap

* 12 - improve getLastPriceFromMap to distinguish between buy/sell offers

buy offers should have an offer price < lp whereas sell offer should have an offer price > lp

* 13 - fix ccxt_test.go and add poloniex as a tested exchange

* 14 - update README and config file for additional documentation and tips

* 15 - update swing strategy to use hProcool.Asset

* 16 - simplify config file by removing OFFSET_SPREAD configuration and adding more comments on spread intuition

* 17 - rename "swing" strategy to "pendulum" strategy, in all files and code

- avoid confusion with "swing" trading, while retaining the concept of a swinging pendulum

* 18 - add walkthough guide for pendulum strategy

* 19 - fix SortOrder of the strategy

* 20 - add tests for strategies and exchanges commands

* 21 - inject sentinel values for build variables in circleci config
  • Loading branch information
nikhilsaraf authored Jun 1, 2020
1 parent 2abdc0b commit 28732f0
Show file tree
Hide file tree
Showing 16 changed files with 762 additions and 46 deletions.
2 changes: 1 addition & 1 deletion .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ commands:
steps:
- run:
name: Run Kelp tests
command: go test --short ./...
command: go test --short -ldflags="-X github.com/stellar/kelp/cmd.version=test_compile -X github.com/stellar/kelp/cmd.guiVersion=test_compile -X github.com/stellar/kelp/cmd.gitBranch=test_compile -X github.com/stellar/kelp/cmd.gitHash=test_compile -X github.com/stellar/kelp/cmd.buildDate=test_compile -X github.com/stellar/kelp/cmd.env=dev" ./...

build_kelp:
steps:
Expand Down
16 changes: 13 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ These are the following commands available from the `kelp` binary:
The `trade` command has three required parameters which are:

- **botConf**: full path to the _.cfg_ file with the account details, [sample file here](examples/configs/trader/sample_trader.cfg).
- **strategy**: the strategy you want to run (_sell_, _buysell_, _balanced_, _mirror_, _delete_).
- **strategy**: the strategy you want to run (_sell_, _buysell_, _balanced_, _pendulum_, _mirror_, _delete_).
- **stratConf**: full path to the _.cfg_ file specific to your chosen strategy, [sample files here](examples/configs/trader/).

Kelp sets the `X-App-Name` and `X-App-Version` headers on requests made to Horizon. These headers help us track overall Kelp usage, so that we can learn about general usage patterns and adapt Kelp to be more useful in the future. These can be turned off using the `--no-headers` flag. See `kelp trade --help` for more information.
Expand Down Expand Up @@ -218,11 +218,19 @@ The following strategies are available **out of the box** with Kelp:
- **Complexity:** Beginner

- balanced ([source](plugins/balancedStrategy.go)):
- **What:** dynamically prices two tokens based on their relative demand. For example, if more traders buy token A _from_ the bot (the traders are therefore selling token B), the bot will automatically raise the price for token A and drop the price for token B.

- **What:** dynamically prices two tokens based on their relative demand. For example, if more traders buy token A _from_ the bot (the traders are therefore selling token B), the bot will automatically raise the price for token A and drop the price for token B. This strategy does not allow you to configure the order size but can run out of assets. This is a mean-reversion strategy.
- **Why:** To let the market surface the _true price_ for one token in terms of another.
- **Who:** Market makers and traders for tokens that trade only on Stellar
- **Who:** Market makers and traders for tokens that have a neutral view on the market
- **Complexity:** Intermediate

- pendulum ([source](plugins/pendulumStrategy.go)):

- **What:** dynamically prices two tokens based on their relative demand. For example, if more traders buy token A _from_ the bot (the traders are therefore selling token B), the bot will automatically raise the price for token A and drop the price for token B. This strategy allows you to configure the order size but runs the risk of running out of one of the two assets. This is a mean-reversion strategy.
- **Why:** To let the market surface the _true price_ for one token in terms of another.
- **Who:** Market makers and traders for tokens that have a neutral view on the market
- **Complexity:** Beginner

- mirror ([source](plugins/mirrorStrategy.go)):

- **What:** mirrors an orderbook from another exchange by placing the same orders on Stellar after including a [spread][spread].
Expand Down Expand Up @@ -327,6 +335,7 @@ It's easier to learn with examples! Take a look at the walkthrough guides and sa
- [Market making for a stablecoin](examples/walkthroughs/trader/buysell.md): This guide uses the _buysell_ strategy to provide liquidity for a stablecoin.
- [ICO sale](examples/walkthroughs/trader/sell.md): This guide uses the `sell` strategy to make a market using sell offers for native tokens in a hypothetical ICO.
- [Create liquidity for a Stellar-based token](examples/walkthroughs/trader/balanced.md): This guide uses the `balanced` strategy to create liquidty for a token which only trades on the Stellar network.
- [Create wide liquidity within a bounded price range](examples/walkthroughs/trader/pendulum.md): This guide uses the `pendulum` strategy to create liquidty for a token.

## Configuration Files

Expand All @@ -335,6 +344,7 @@ Reference config files are in the [examples folder](examples/configs/trader). Sp
- [Sample Sell strategy config file](examples/configs/trader/sample_sell.cfg)
- [Sample BuySell strategy config file](examples/configs/trader/sample_buysell.cfg)
- [Sample Balanced strategy config file](examples/configs/trader/sample_balanced.cfg)
- [Sample Pendulum strategy config file](examples/configs/trader/sample_pendulum.cfg)
- [Sample Mirror strategy config file](examples/configs/trader/sample_mirror.cfg)

# Changelog
Expand Down
4 changes: 2 additions & 2 deletions cmd/exchanges.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@ import (
"github.com/spf13/cobra"
)

var exchanagesCmd = &cobra.Command{
var exchangesCmd = &cobra.Command{
Use: "exchanges",
Short: "Lists the available exchange integrations",
}

func init() {
exchanagesCmd.Run = func(ccmd *cobra.Command, args []string) {
exchangesCmd.Run = func(ccmd *cobra.Command, args []string) {
checkInitRootFlags()
// call sdk.GetExchangeList() here so we pre-load exchanges before displaying the table
sdk.GetExchangeList()
Expand Down
9 changes: 9 additions & 0 deletions cmd/exchanges_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package cmd

import (
"testing"
)

func TestExchanges(t *testing.T) {
exchangesCmd.Run(nil, nil)
}
2 changes: 1 addition & 1 deletion cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ func init() {
RootCmd.AddCommand(tradeCmd)
RootCmd.AddCommand(serverCmd)
RootCmd.AddCommand(strategiesCmd)
RootCmd.AddCommand(exchanagesCmd)
RootCmd.AddCommand(exchangesCmd)
RootCmd.AddCommand(terminateCmd)
RootCmd.AddCommand(versionCmd)
}
Expand Down
9 changes: 9 additions & 0 deletions cmd/strategies_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package cmd

import (
"testing"
)

func TestStrategies(t *testing.T) {
strategiesCmd.Run(nil, nil)
}
14 changes: 13 additions & 1 deletion cmd/trade.go
Original file line number Diff line number Diff line change
Expand Up @@ -324,7 +324,19 @@ func makeStrategy(
deleteAllOffersAndExit(l, botConfig, client, sdex, exchangeShim, threadTracker)
}

strategy, e := plugins.MakeStrategy(sdex, ieif, tradingPair, &assetBase, &assetQuote, *options.strategy, *options.stratConfigPath, *options.simMode)
strategy, e := plugins.MakeStrategy(
sdex,
exchangeShim,
exchangeShim,
ieif,
tradingPair,
&assetBase,
&assetQuote,
*options.strategy,
*options.stratConfigPath,
*options.simMode,
botConfig.IsTradingSdex(),
)
if e != nil {
l.Info("")
l.Errorf("%s", e)
Expand Down
53 changes: 53 additions & 0 deletions examples/configs/trader/sample_pendulum.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# Sample config file for the "pendulum" strategy

# what % deviation from the ideal price is allowed before we reset the price, specified as a decimal (0 < PRICE_TOLERANCE < 1.00)
PRICE_TOLERANCE=0.001

# what % deviation from the ideal amount is allowed before we reset the price, specified as a decimal (0 < AMOUNT_TOLERANCE < 1.00)
AMOUNT_TOLERANCE=1.0

# Amounts
# Note: advanced users could adjust these numbers to effectively control how much of any result from each rountrip trade (buy followed
# by sell, or sell followed by buy) ends up in the base asset vs. the quote asset. In order to control this you need to account for
# the fee paid to the exchange and your spread setting (below).
# When the buy and sell amounts are configured to the same value then all of any result from each roundtrip trade will be in the quote
# asset.
# AMOUNT_BASE_BUY is the amount to place denominated in the base asset for the buy side
AMOUNT_BASE_BUY=100.0
# AMOUNT_BASE_SELL is the amount to place denominated in the base asset for the sell side
AMOUNT_BASE_SELL=100.0

# define the bid/ask spread that you are willing to provide.
# spread is a percentage specified as a decimal number (0 < spread < 1.00) - here it is 0.1%
#
# Note 1: the resting bid and ask orders will have a larger spread than what is specified here. The reason is that the bids and
# asks adjust automatically by moving up/down when orders are taken. If an ask is taken then all bid and ask orders move up.
# If a bid is taken then all bid and ask orders move down.
# This SPREAD is the effective spread percent you will receive.
#
# Note 2: this spread value should be greater than or equal to (2 x fee) on the exchange you are trading on.
# The intuition behind this is that in order to complete a roundtrip (buy followed by sell, or sell followed by buy), you will
# make two trades which will cost you (2 x fee) as a percentage of your order size.
# By setting a spread value greater than or equal to (2 x fee) you are accounting for the fees as a cost of your trading activities.
SPREAD=0.001

# max number of levels to have on either side. Defines how deep of an orderbook you want to make.
MAX_LEVELS=2

# Price Limits to control for Market Conditions changing
# It is required to set the seed price otherwise the algorithm will not work. It is recommended to set the min/max price so if market
# conditions change and there is an extreme spike in the value of one asset relative to the other then the bot will pause trading.
# (recommended) maximum price to offer, without this setting you could end up at a price where your algorithm is no longer effective
MAX_PRICE=0.070
# (required) price with which to start off as the last trade price (i.e. initial center price)
SEED_LAST_TRADE_PRICE=0.066
# (recommended) minimum price to offer, without this setting you could end up at a price where your algorithm is no longer effective
MIN_PRICE=0.062

# minimum amount of base asset balance to maintain after which the strategy won't place any more orders
MIN_BASE=0.0
# minimum amount of quote asset balance to maintain after which the strategy won't place any more orders
MIN_QUOTE=0.0

# cursor from where to start fetching fills. If left blank then it will fetch from the first trade
#LAST_TRADE_CURSOR="TX_ID"
103 changes: 103 additions & 0 deletions examples/walkthroughs/trader/pendulum.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
# Create Wide Liquidity Within A Bounded Price Range

This guide shows you how to setup the **kelp** bot using the [pendulum](../../../plugins/pendulumStrategy.go) strategy. We'll configure it to create liquidity for a `COUPON` token. `COUPON` can be any token you want and is only used as a sample token for the purpose of this walkthrough guide.

The bot dynamically prices two tokens based on their relative demand. When someone _buys_ the [base asset](https://en.wikipedia.org/wiki/Currency_pair#Base_currency) from the bot (i.e. the bot's ask order is executed resulting in the bot selling the base asset), the bot will _increase_ the price of the base asset relative to the counter asset. Conversely, when someone _sells_ the [base asset](https://en.wikipedia.org/wiki/Currency_pair#Base_currency) to the bot (i.e. the bot's bid order is executed resulting in the bot buying the base asset), the bot will _decrease_ the price of the base asset relative to the counter asset.

This strategy operates like a pendulum in that if a trader hits the ask then the bot moves the price to the right side (higher), if a trader hits the bid then the bot moves the price to the left side (lower).

## Account Setup

First, go through the [Account Setup guide](account_setup.md) to set up your Stellar accounts and the necessary configuration file, `trader.cfg`.

## Install Bots

Download the pre-compiled binaries for **kelp** for your platform from the [Github Releases Page](https://github.com/stellar/kelp/releases). If you have downloaded the correct version for your platform you can run it directly.

## Pendulum Strategy Configuration

Use the [sample configuration file for the pendulum strategy](../../configs/trader/sample_pendulum.cfg) as a template. We will walk through the configuration parameters below.

### Tolerances

For the purposes of this walkthrough, we set the `PRICE_TOLERANCE` value to `0.001` which means that a _0.1% change in price will trigger the bot to refresh its orders_. Similarly, we set the `AMOUNT_TOLERANCE` value to `1.0` which means that we need _at least a 100% change in amount will trigger the bot to refresh its orders_, i.e. an order needs to be fully consumed to trigger a replacement of that order. If either one of these conditions is met then the bot will refresh its orders.

In practice, you should set any value you're comfortable with.

### Amounts

- **`AMOUNT_BASE_BUY`**: refers to the order size denominated in units of the base asset to be placed for the bids, represented as a decimal number.
- **`AMOUNT_BASE_SELL`**: refers to the order size denominated in units of the base asset to be placed for the asks, represented as a decimal number.

### Spread

- **`SPREAD`**: refers to the [bid/ask spread](https://en.wikipedia.org/wiki/Bid%E2%80%93ask_spread) as a percentage represented as a decimal number (`0.0` < spread < `1.00`).

**Note 1**: the resting bid and ask orders will have a larger spread than what is specified in the config.
The reason is that the bids and asks adjust automatically by moving up/down when orders are taken as described above.
If an ask is taken then all bid and ask orders move up. If a bid is taken then all bid and ask orders move down.
This `SPREAD` config value is the effective spread percent you will receive after adjusting for the automatic movement of orders, assuming that the bot has time to move the orders before the next trade happens. If the bot does not have time to move the orders then the bot will receive a larger spread than what is specified in the config.

**Note 2**: this spread value percent should be greater than or equal to **2 x fee** on the exchange you are trading on.
The intuition behind this is that in order to complete a roundtrip (buy followed by sell, or sell followed by buy), you will make two trades which will cost you **2 x fee** as a percentage of your order size.
By setting a spread value greater than or equal to **2 x fee** you are accounting for the fees as a cost of your trading activities.

### Levels

A level defines a [layer](https://en.wikipedia.org/wiki/Layering_(finance)) that sits in the orderbook. The bot will create mirrored orders on both the buy side and the sell side for each level configured.

- **`MAX_LEVELS`**: refers to the number of levels that you want on either side of the mid-price.

![level screenshot](https://i.imgur.com/QVjZXGA.png "Levels Screenshot")

### Price Limits

It is important to set price limits to control for changing market conditions. **It is highly recommended to set all three of these values. It is extremely dangerous to not set them.**

- **`SEED_LAST_TRADE_PRICE`**: (required) price with which to start off as the last trade price (i.e. initial center price). A good value for this is the current mid-price of the market you are trading on, but it is not always the best choice.
- **`MAX_PRICE`**: maximum price to offer, without this setting you could end up at a price where your algorithm is no longer effective
- **`MIN_PRICE`**: minimum price to offer, without this setting you could end up at a price where your algorithm is no longer effective

**Note 1**: You are required to set the `SEED_LAST_TRADE_PRICE` otherwise the algorithm will not work. **it is highly recommend to always update the value of `SEED_LAST_TRADE_PRICE` in the configuration before starting a new run of the bot so you can ensure it is line with the current market price.**

**Note 2**: It is recommended to set the `MAX_PRICE` and the `MIN_PRICE` to define the bounds in which your algorithm will work as expected. If market conditions change to the point where the price of the market goes outside this range then your configuration is no longer valid and it is better for your bot to pause trading.
This can be caused by a spike in the relative value of one asset compared to the other, which is not conducive to this trading strategy and you should re-evaluate and update your configuration in this situation, or consider stopping your trading activities on this market altogether.

### Minimum Amount Limits

You may want to ensure that your account has a minimum balance of a given asset so you do not risk running out of any one asset. These settings help you configure that.

- **`MIN_BASE`**: decimal value representing the minimum amount of base asset balance to maintain after which the strategy won't place any more orders
- **`MIN_QUOTE`**: decimal value representing the minimum amount of quote asset balance to maintain after which the strategy won't place any more orders

### Historical Trades

This trading strategy adjusts the offered price based on the last price it received for a trade. In order to do this it needs to fetch trades from the exchange. In order to do this the bot will need to know from which point to start fetching trades (_cursor_).

If this value is left empty then it will fetch all the trades for your account for the given market which may be excessive and can result in your bot hitting or exceeding the rate limit. This configuration helps you to set the starting point from where to fetch trades so that you do not fetch more trades than you need to.

- **`LAST_TRADE_CURSOR`**: cursor from where to start fetching trades. If left blank then it will fetch from the first trade made on the specified market.

**Note 1**: The `LAST_TRADE_CURSOR` should be specified in the same format as defined by your exchange. On SDEX this can be the paging token, on Kraken this can be your transaction ID, on binance this may be your timestamp etc. You will need to figure out the value to be used. The log files for this trading strategy displays the trades as they happen which includes the trade cursor for each trade entry and can be used to fill in the `LAST_TRADE_CURSOR` value. At each update interval of the bot it logs the currently held value for `LAST_TRADE_CURSOR`, which can also be used to update this configuration value when resuming the bot after it has been paused.

**Note 2**: The first time that the bot fetches trades from the cursor specified in the `LAST_TRADE_CURSOR` at startup, it will update the value held in memory for `LAST_TRADE_CURSOR` but will not use the price from these values retrieved to update the bot's orders because it will use the price set in the `SEED_LAST_TRADE_PRICE` (configured above) for the initial run of the bot. This allows you to set a new price for the bot via the `SEED_LAST_TRADE_PRICE` configuration parameter if you are resuming the bot under new market conditions compared to the last run of your bot. For every subsequent trade it will update the vale of `SEED_LAST_TRADE_PRICE` along with `LAST_TRADE_CURSOR` held in memory. This behavior allows you to leave the `LAST_TRADE_CURSOR` setting as-is if your bot has not seen many trades (i.e. for short runs of the bot). Although, it is highly recommend to always update the value of `LAST_TRADE_CURSOR` in the configuration before starting your bot.

## Comparison to Balanced Strategy

This strategy functions similarly to the [balanced strategy](balanced.md) but gives you the ability to control the order size (amount).

Another benefit of this strategy over the balanced strategy is that you do not need a fixed ratio of balances of your assets to begin trading. The amount and initial price is set in the configuration file directly instead of being computed from the balances of the assets in the account, which makes this strategy more flexible than the balanced strategy.

However, one of the tradeoffs of this additional flexibility is that this strategy can run out of one of the assets. To safeguard from this, you can set up _Price Limits_ and _Minimum Amount Limits_ as described in the configuration sections above.

## Run Kelp

Assuming your botConfig is called `trader.cfg` and your strategy config is called `pendulum.cfg`, you can run `kelp` with the following command:

```
kelp trade --botConf ./path/trader.cfg --strategy pendulum --stratConf ./path/pendulum.cfg
```

# Above and Beyond

You can also play around with the configuration parameters of the [sample configuration file for the pendulum strategy](../../configs/trader/sample_pendulum.cfg), look at some of the other strategies that are available out-of-the-box or dig into the code and _create your own strategy_.
Loading

0 comments on commit 28732f0

Please sign in to comment.