-
-
Save idler921/5707557 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
The portfolio rebalancing bot will buy and sell to maintain a | |
constant asset allocation ratio of exactly 50/50 = fiat/BTC | |
""" | |
import strategy | |
import goxapi | |
DISTANCE = 7 # percent price distance of next rebalancing orders | |
MARKER = 7 # lowest digit of price to identify bot's own orders | |
COIN = 1E8 # number of satoshi per coin, this is a constant. | |
def add_marker(price, marker): | |
"""encode a marker in the price value to find bot's own orders""" | |
return price / 10 * 10 + marker | |
def has_marker(price, marker): | |
"""return true if the price value has the marker""" | |
return (price % 10) == marker | |
def mark_own(price): | |
"""return the price with our own marker embedded""" | |
return add_marker(price, MARKER) | |
def is_own(price): | |
"""return true if this price has our own marker""" | |
return has_marker(price, MARKER) | |
class Strategy(strategy.Strategy): | |
"""a protfolio rebalancing bot""" | |
def __init__(self, gox): | |
strategy.Strategy.__init__(self, gox) | |
self.last_trade = 0 | |
def slot_before_unload(self, _sender, _data): | |
pass | |
def slot_keypress(self, gox, (key)): | |
"""a key has been pressed""" | |
if key == ord("c"): | |
# remove existing rebalancing orders | |
self.debug("canceling all rebalancing orders") | |
self.cancel_orders() | |
if key == ord("p"): | |
# create the initial two rebalancing orders. Before you | |
# do this the portfolio should already be balanced. | |
self.debug("adding new initial rebalancing orders") | |
book = self.gox.orderbook | |
self.place_orders((book.bid + book.ask) / 2) | |
def cancel_orders(self): | |
"""cancel all rebalancing orders, we identify | |
them through the marker in the price value""" | |
must_cancel = [] | |
for order in self.gox.orderbook.owns: | |
if is_own(order.price): | |
must_cancel.append(order) | |
for order in must_cancel: | |
self.gox.cancel(order.oid) | |
def get_buy_at_price(self, price_int): | |
"""calculate amount of BTC needed to buy at price to achieve | |
rebalancing. price and return value are in mtgox integer format""" | |
currency = self.gox.currency | |
fiat_have = goxapi.int2float(self.gox.wallet[currency], currency) | |
btc_have = goxapi.int2float(self.gox.wallet["BTC"], "BTC") | |
price_then = goxapi.int2float(price_int, currency) | |
btc_value_then = btc_have * price_then | |
diff = fiat_have - btc_value_then | |
diff_btc = diff / price_then | |
must_buy = diff_btc / 2 | |
return goxapi.float2int(must_buy, "BTC") | |
def place_orders(self, center): | |
"""place two new rebalancing orders above and below center price""" | |
currency = self.gox.currency | |
step = int(center * DISTANCE / 100.0) | |
next_sell = mark_own(center + step) | |
next_buy = mark_own(center - step) | |
sell_amount = -self.get_buy_at_price(next_sell) | |
buy_amount = self.get_buy_at_price(next_buy) | |
if sell_amount < 0.01 * COIN: | |
sell_amount = int(0.01 * COIN) | |
self.debug("WARNING! minimal sell amount adjusted to 0.01") | |
if buy_amount < 0.01 * COIN: | |
buy_amount = int(0.01 * COIN) | |
self.debug("WARNING! minimal buy amount adjusted to 0.01") | |
self.debug("new buy order %f at %f" % ( | |
goxapi.int2float(buy_amount, "BTC"), | |
goxapi.int2float(next_buy, currency) | |
)) | |
self.gox.buy(next_buy, buy_amount) | |
self.debug("new sell order %f at %f" % ( | |
goxapi.int2float(sell_amount, "BTC"), | |
goxapi.int2float(next_sell, currency) | |
)) | |
self.gox.sell(next_sell, sell_amount) | |
def slot_trade(self, gox, (date, price, volume, typ, own)): | |
"""a trade message has been receivd""" | |
# not interested in other people's trades | |
if not own: | |
return | |
# not interested in manually entered (not bot) trades | |
if not is_own(price): | |
return | |
self.last_trade = price | |
self.debug("trade (type: %s) detected!" % typ) | |
self.check_trades() | |
def slot_owns_changed(self, orderbook, _dummy): | |
"""status or amount of own open orders has changed""" | |
self.check_trades() | |
def check_trades(self): | |
"""find out if we need to place new orders and do it if neccesary""" | |
# don't place double orders. only act if a trade has happened. | |
if not self.last_trade: | |
return | |
# since we have always exactly 2 orders, a bid and an ask we | |
# must count the open orders and we know we must act when there | |
# is exactly one open order. | |
count = 0 | |
for order in self.gox.orderbook.owns: | |
if is_own(order.price): | |
count += 1 | |
# if count is exacty 1 then one of the orders must have been filled, | |
# now we cancel the other one and place two fresh orders in the | |
# distance of DISTANCE around current price. | |
if count == 1: | |
self.cancel_orders() | |
self.place_orders(self.last_trade) | |
# reset the flag again | |
self.last_trade = 0 |
Hi!
What effect would 20/80 have over 50/50, would it grow Bitcoin faster? Thanks for sharing.
i get this error
File 'home/user/goxtool/goxapi.py', line 385, in call
func(instance, sender, data)
File '/home/user/goxtool/_balancer.py', line 60, in slot_keypress
self.place_orders()
File '/home/user/goxtool/_balancer.py', line 135, in place_orders
center = self.get_price_where_it_was_balanced()
File '/home/user/goxtool/_balancer.py', line 117, in get_price_where_it_was_balanced
return goxapi.float2int(fiat_have * bfr / btc_have , currency)
ZeroDivisionError: float division by zero
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
You have a typo there: FAITLV should probably mean FIATLV
Also you should make these constants float, otherwise it might produce strange results because divisions of integers will be integer divisions. In this case it does not matter because 8/2=4 but if someone uses 7/3 it will produce incorrect results. initialitze them like so:
FIATLV = 2.0
BTCLV = 8.0