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()
|