Wall Strategy

This strategy simply places a buy and a sell wall into a specific market using a specified account.

Example Configuration

# BitShares end point
node: "wss://node.bitshares.eu"

# List of Bots
bots:

        # Only a single Walls Bot
        Walls:

             # The Walls strategy module and class
             module: dexbot.strategies.walls
             bot: Walls

             # The market to serve
             market: HERO:BTS

             # The account to sue
             account: hero-market-maker

             # We shall bundle operations into a single transaction
             bundle: True

             # Test your conditions every x blocks
             test:
                     blocks: 10

             # Where the walls should be
             target:

                     # They relate to the price feed
                     reference: feed

                     # There should be an offset
                     offsets:
                         buy: 2.5
                         sell: 2.5

                     # We'd like to use x amount of quote (here: HERO)
                     # in the walls
                     amount:
                         buy: 5.0
                         sell: 5.0

             # When the price moves by more than 2%, update the walls
             threshold: 2

Source Code

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
from math import fabs
from collections import Counter
from bitshares.amount import Amount
from dexbot.basestrategy import BaseStrategy, ConfigElement
from dexbot.errors import InsufficientFundsError


class Strategy(BaseStrategy):
    """
    Walls strategy
    """

    @classmethod
    def configure(cls):

        return BaseStrategy.configure() + [
            ConfigElement(
                "spread",
                "int",
                5,
                "the spread between sell and buy as percentage",
                (0,
                 100)),
            ConfigElement(
                "threshold",
                "int",
                5,
                "percentage the feed has to move before we change orders",
                (0,
                 100)),
            ConfigElement(
                "buy", "float", 0.0, "the default amount to buy", (0.0, None)),
            ConfigElement("sell", "float", 0.0,
                          "the default amount to sell", (0.0, None)),
            ConfigElement(
                "blocks",
                "int",
                20,
                "number of blocks to wait before re-calculating",
                (0,
                 10000)),
            ConfigElement(
                "dry_run",
                "bool",
                False,
                "Dry Run Mode\nIf Yes the bot won't buy or sell anything, just log what it would do.\nIf No, the bot will buy and sell for real.",
                None)
        ]

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        # Define Callbacks
        self.onMarketUpdate += self.test
        self.ontick += self.tick
        self.onAccount += self.test

        self.error_ontick = self.error
        self.error_onMarketUpdate = self.error
        self.error_onAccount = self.error

        # Counter for blocks
        self.counter = Counter()

        # Tests for actions
        self.test_blocks = self.worker.get("test", {}).get("blocks", 0)

    def error(self, *args, **kwargs):
        self.disabled = True
        self.cancelall()
        self.log.info(self.execute())

    def updateorders(self):
        """ Update the orders
        """
        self.log.info("Replacing orders")

        # Canceling orders
        self.cancelall()

        # Target
        target = self.worker.get("target", {})
        price = self.getprice()

        # prices
        buy_price = price * (1 - target["offsets"]["buy"] / 100)
        sell_price = price * (1 + target["offsets"]["sell"] / 100)

        # Store price in storage for later use
        self["feed_price"] = float(price)

        # Buy Side
        if float(self.balance(self.market["base"])
                 ) < buy_price * target["amount"]["buy"]:
            InsufficientFundsError(
                Amount(
                    target["amount"]["buy"] *
                    float(buy_price),
                    self.market["base"]))
            self["insufficient_buy"] = True
        else:
            self["insufficient_buy"] = False
            self.market.buy(
                buy_price,
                Amount(target["amount"]["buy"], self.market["quote"]),
                account=self.account
            )

        # Sell Side
        if float(self.balance(self.market["quote"])
                 ) < target["amount"]["sell"]:
            InsufficientFundsError(
                Amount(
                    target["amount"]["sell"],
                    self.market["quote"]))
            self["insufficient_sell"] = True
        else:
            self["insufficient_sell"] = False
            self.market.sell(
                sell_price,
                Amount(target["amount"]["sell"], self.market["quote"]),
                account=self.account
            )

        self.log.info(self.execute())

    def getprice(self):
        """ Here we obtain the price for the quote and make sure it has
            a feed price
        """
        target = self.worker.get("target", {})
        if target.get("reference") == "feed":
            assert self.market == self.market.core_quote_market(
            ), "Wrong market for 'feed' reference!"
            ticker = self.market.ticker()
            price = ticker.get("quoteSettlement_price")
            assert abs(price["price"]) != float(
                "inf"), "Check price feed of asset! (%s)" % str(price)
        return price

    def tick(self, d):
        """ ticks come in on every block
        """
        if self.test_blocks:
            if not (self.counter["blocks"] or 0) % self.test_blocks:
                self.test()
            self.counter["blocks"] += 1

    def test(self, *args, **kwargs):
        """ Tests if the orders need updating
        """
        orders = self.orders

        # Test if still 2 orders in the market (the walls)
        if len(orders) < 2 and len(orders) > 0:
            if (
                not self["insufficient_buy"] and
                not self["insufficient_sell"]
            ):
                self.log.info("No 2 orders available. Updating orders!")
                self.updateorders()
        elif len(orders) == 0:
            self.updateorders()

        # Test if price feed has moved more than the threshold
        if (
            self["feed_price"] and
            fabs(1 - float(self.getprice()) / self["feed_price"]) > self.worker["threshold"] / 100.0
        ):
            self.log.info(
                "Price feed moved by more than the threshold. Updating orders!")
            self.updateorders()