From e0e26dfcf1b4b7debcab233631ce5c5443f06633 Mon Sep 17 00:00:00 2001 From: Fabian Schuh Date: Thu, 21 Jan 2016 17:05:11 +0100 Subject: [PATCH] [ExchangeBot] Now does what it is supposed to be doing --- scripts/exchange-bots/bot/bot.py | 26 +++- .../bot/strategies/basestrategy.py | 85 +++++++---- .../bot/strategies/bridgemaker.py | 127 ++++++++++++++-- scripts/exchange-bots/bot/strategies/maker.py | 141 ++++++++++++++++++ scripts/exchange-bots/data_MakerRamp.json | 1 + scripts/exchange-bots/data_MakerRamp2.json | 1 + scripts/exchange-bots/main.py | 37 +---- 7 files changed, 337 insertions(+), 81 deletions(-) create mode 100644 scripts/exchange-bots/bot/strategies/maker.py create mode 100644 scripts/exchange-bots/data_MakerRamp.json create mode 100644 scripts/exchange-bots/data_MakerRamp2.json diff --git a/scripts/exchange-bots/bot/bot.py b/scripts/exchange-bots/bot/bot.py index e9a92c48..35fc9688 100644 --- a/scripts/exchange-bots/bot/bot.py +++ b/scripts/exchange-bots/bot/bot.py @@ -1,21 +1,35 @@ +from grapheneapi.graphenewsprotocol import GrapheneWebsocketProtocol from grapheneexchange import GrapheneExchange +import time + + +class BotProtocol(GrapheneWebsocketProtocol): + pass class Bot(): def __init__(self, config, **kwargs): + botProtocol = BotProtocol + [setattr(botProtocol, key, config.__dict__[key]) for key in config.__dict__.keys()] self.config = config - self.dex = GrapheneExchange(config, safe_mode=True) + self.dex = GrapheneExchange(botProtocol, safe_mode=config.safe_mode) # Initialize all bots self.bots = {} - for name in config.bots: - self.bots[name] = config.bots[name]["bot"](config=self.config, - name=name, - dex=self.dex) + for index, name in enumerate(config.bots, 1): + botClass = config.bots[name]["bot"] + self.bots[name] = botClass(config=config, name=name, + dex=self.dex, index=index) self.bots[name].init() + def wait_block(self): + time.sleep(6) + def execute(self): for name in self.bots: - self.bots[name].cancel_mine() + if self.bots[name].cancel_this_markets() and self.config.safe_mode: + self.wait_block() + self.bots[name].tick() + self.bots[name].store() diff --git a/scripts/exchange-bots/bot/strategies/basestrategy.py b/scripts/exchange-bots/bot/strategies/basestrategy.py index 90d87ba8..bfb40295 100644 --- a/scripts/exchange-bots/bot/strategies/basestrategy.py +++ b/scripts/exchange-bots/bot/strategies/basestrategy.py @@ -1,11 +1,12 @@ from grapheneexchange import GrapheneExchange import json +import os class BaseStrategy(): def __init__(self, *args, **kwargs): - self.state = None + self.state = {"orders" : []} for arg in args : if isinstance(arg, GrapheneExchange): @@ -16,9 +17,9 @@ def __init__(self, *args, **kwargs): if "name" not in kwargs: raise ValueError("Missing parameter 'name'!") self.filename = "data_%s.json" % self.name - self.settings = self.config.bots[self.name] - self.orders = [] + + self.restore() def init(self) : print("Initializing %s" % self.name) @@ -27,30 +28,42 @@ def tick(self) : pass def cancel_all(self) : - orders = self.dex.returnOpenOrders() - for m in orders: + onceCanceled = False + curOrders = self.dex.returnOpenOrdersIds() + for m in curOrders: for order in orders[m]: - self.dex.cancel(order["orderNumber"]) + try : + self.dex.cancel(order["orderNumber"]) + onceCanceled = True + except: + print("An error has occured when trying to cancel order %s!" % order["orderNumber"]) + return onceCanceled def cancel_mine(self) : - myOrders = [] - for i, d in enumerate(self.orders): - o = {} - o["for_sale"] = d["amount_to_sell"] - myOrders.append(o) - + curOrders = self.dex.returnOpenOrdersIds() + state = self.getState() + onceCanceled = False + for o in state["orders"]: + for m in curOrders: + if o in curOrders[m] : + try : + self.dex.cancel(o) + onceCanceled = True + except: + print("An error has occured when trying to cancel order %s!" % o["orderNumber"]) + return onceCanceled + + def cancel_this_markets(self) : orders = self.dex.returnOpenOrders() - for m in orders: + onceCanceled = False + for m in self.settings["markets"]: for order in orders[m]: - for stored_order in myOrders: - print("==========") - print(stored_order["for_sale"]) - print(order["amount_to_sell"]) -# #self.dex.cancel(order["orderNumber"]) - - def save_orders(self, orders): - for o in orders["operations"] : - self.orders.append(o[1]) + try : + self.dex.cancel(order["orderNumber"]) + onceCanceled = True + except: + print("An error has occured when trying to cancel order %s!" % order["orderNumber"]) + return onceCanceled def place(self) : pass @@ -62,11 +75,31 @@ def setState(self, state): self.state = state def store(self): - state = self.state() + state = self.getState() + + orders = [] + curOrders = self.dex.returnOpenOrdersIds() + for m in curOrders : + for cur in curOrders[m] : + if cur not in state["orders"] : + orders.append(cur) + + state["orders"] = orders with open(self.filename, 'w') as fp: json.dump(state, fp) def restore(self): - with open(self.filename, 'r') as fp: - state = json.load(fp) - self.setState(state) + if os.path.isfile(self.filename) : + with open(self.filename, 'r') as fp: + state = json.load(fp) + self.setState(state) + + def sell(self, market, price, amount): + quote, base = market.split(self.config.market_separator) + print(" - Selling %f %s for %s @%f %s/%s" % (amount, quote, base, price, quote, base)) + self.dex.sell(market, price, amount) + + def buy(self, market, price, amount): + quote, base = market.split(self.config.market_separator) + print(" - Buying %f %s with %s @%f %s/%s" % (amount, base, quote, price, quote, base)) + self.dex.buy(market, price, amount) diff --git a/scripts/exchange-bots/bot/strategies/bridgemaker.py b/scripts/exchange-bots/bot/strategies/bridgemaker.py index c490bfd0..3e933876 100644 --- a/scripts/exchange-bots/bot/strategies/bridgemaker.py +++ b/scripts/exchange-bots/bot/strategies/bridgemaker.py @@ -1,4 +1,6 @@ from .basestrategy import BaseStrategy +import math +from numpy import linspace """ """ @@ -9,8 +11,47 @@ class BridgeMaker(BaseStrategy): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) + def tick(self) : + self.cancel_mine() + self.place() + + def place(self) : + print("Placing Orders:") + buy_price = 1 - self.settings["spread_percentage"] / 200 + sell_price = 1 + self.settings["spread_percentage"] / 200 + + #: Amount of Funds available for trading (per asset) + balances = self.dex.returnBalances() + asset_ids = [] + amounts = {} + for market in self.settings["markets"] : + quote, base = market.split(self.config.market_separator) + asset_ids.append(base) + asset_ids.append(quote) + assets_unique = list(set(asset_ids)) + for a in assets_unique: + if a in balances : + amounts[a] = balances[a] * self.settings["volume_percentage"] / 100 / asset_ids.count(a) + + for m in self.settings["markets"]: + quote, base = m.split(self.config.market_separator) + if quote in amounts : + self.sell(m, sell_price, amounts[quote]) + if base in amounts : + self.buy(m, buy_price, amounts[base] * buy_price) + + +class BridgeMakerEqual(BaseStrategy): + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + def tick(self) : + self.cancel_mine() + self.place() + def place(self) : - print("Placing Orders") + print("Placing Orders:") buy_price = 1 - self.settings["spread_percentage"] / 200 sell_price = 1 + self.settings["spread_percentage"] / 200 @@ -18,7 +59,7 @@ def place(self) : balances = self.dex.returnBalances() asset_ids = [] amounts = {} - for market in self.config.watch_markets : + for market in self.settings["markets"] : quote, base = market.split(self.config.market_separator) asset_ids.append(base) asset_ids.append(quote) @@ -27,18 +68,76 @@ def place(self) : if a in balances : amounts[a] = balances[a] * self.settings["volume_percentage"] / 100 / asset_ids.count(a) + for m in self.settings["markets"]: + quote, base = m.split(self.config.market_separator) + if quote in amounts : + self.sell(m, sell_price, min([amounts[quote], amounts[base] * buy_price])) + if base in amounts : + self.buy(m, buy_price, min([amounts[quote], amounts[base] * buy_price])) + + +class BridgeMakerRamp(BaseStrategy): + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + def tick(self) : + self.cancel_mine() + self.place() + + def place(self) : print("Placing Orders:") - orders = [] - for m in self.config.watch_markets: + #: Amount of Funds available for trading (per asset) + if "ramp_mode" not in self.settings: + mode = "linear" + else : + mode = self.settings["ramp_mode"] + + balances = self.dex.returnBalances() + asset_ids = [] + amounts = {} + for market in self.settings["markets"]: + quote, base = market.split(self.config.market_separator) + asset_ids.append(base) + asset_ids.append(quote) + assets_unique = list(set(asset_ids)) + for a in assets_unique: + if a in balances : + amounts[a] = balances[a] * self.settings["volume_percentage"] / 100 / asset_ids.count(a) + + for m in self.settings["markets"]: + settlement_price = 1.0 quote, base = m.split(self.config.market_separator) if quote in amounts : - print(" - Selling %f %s for %s @%f" % (amounts[quote], quote, base, sell_price)) - tx = self.dex.sell(m, sell_price, amounts[quote]) - self.add_order(tx) - elif base in amounts : - print(" - Buying %f %s with %s @%f" % (amounts[base], base, quote, buy_price)) - tx = self.dex.buy(m, buy_price, amounts[base] * buy_price) - self.add_order(tx) - else: - continue - self.update_orders() + price_start = settlement_price * (1 + self.settings["spread_percentage"] / 200.0) + price_end = settlement_price * (1 + self.settings["ramp_price_percentage"] / 100.0) + amount = min([amounts[quote], amounts[base] * (price_start) / 2.0]) + number_orders = math.floor((self.settings["ramp_price_percentage"] / 100.0 - self.settings["spread_percentage"] / 200.0) / (self.settings["ramp_step_percentage"] / 100.0)) + if mode == "linear" : + for price in linspace(price_start, price_end, number_orders) : + self.sell(m, price, amount / number_orders) + elif mode == "exponential" : + k = linspace(1 / number_orders, 1, number_orders) + k = [v / sum(k) for v in k] + order_amounts = [v * amount for v in k] + for i, price in enumerate(linspace(price_start, price_end, number_orders)): + self.sell(m, price, order_amounts[i]) + else : + raise Exception("ramp_mode '%s' not known" % mode) + + if base in amounts : + price_start = settlement_price * (1 - self.settings["spread_percentage"] / 200.0) + price_end = settlement_price * (1 - self.settings["ramp_price_percentage"] / 100.0) + amount = min([amounts[quote], amounts[base] * (price_start) / 2.0]) + number_orders = math.floor((self.settings["ramp_price_percentage"] / 100.0 - self.settings["spread_percentage"] / 200.0) / (self.settings["ramp_step_percentage"] / 100.0)) + if mode == "linear" : + for price in linspace(price_start, price_end, number_orders) : + self.buy(m, price, amount / number_orders) + elif mode == "exponential" : + k = linspace(1 / number_orders, 1, number_orders) + k = [v / sum(k) for v in k] + order_amounts = [v * amount for v in k] + for i, price in enumerate(linspace(price_start, price_end, number_orders)): + self.buy(m, price, order_amounts[i]) + else : + raise Exception("ramp_mode '%s' not known" % mode) diff --git a/scripts/exchange-bots/bot/strategies/maker.py b/scripts/exchange-bots/bot/strategies/maker.py new file mode 100644 index 00000000..23413af3 --- /dev/null +++ b/scripts/exchange-bots/bot/strategies/maker.py @@ -0,0 +1,141 @@ +from .basestrategy import BaseStrategy +import math +from numpy import linspace + +""" +""" + + +class MakerSellBuyWalls(BaseStrategy): + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + def tick(self) : + #self.cancel_mine() + self.place() + + def place(self) : + print("Placing Orders:") + target_price = self.settings["target_price"] + + #: Amount of Funds available for trading (per asset) + balances = self.dex.returnBalances() + asset_ids = [] + amounts = {} + for market in self.settings["markets"] : + quote, base = market.split(self.config.market_separator) + asset_ids.append(base) + asset_ids.append(quote) + assets_unique = list(set(asset_ids)) + for a in assets_unique: + if a in balances : + amounts[a] = balances[a] * self.settings["volume_percentage"] / 100 / asset_ids.count(a) + + ticker = self.dex.returnTicker() + for m in self.settings["markets"]: + + if isinstance(target_price, float): + buy_price = target_price * (1.0 - self.settings["spread_percentage"] / 200) + sell_price = target_price * (1.0 + self.settings["spread_percentage"] / 200) + elif (isinstance(target_price, str) and + target_price is "settlement_price" or + target_price is "feed" or + target_price is "price_feed"): + if "settlement_price" in ticker[m] : + price = ticker[m]["settlement_price"] + buy_price = price * (1.0 - self.settings["spread_percentage"] / 200) + sell_price = price * (1.0 + self.settings["spread_percentage"] / 200) + else : + raise Exception("Pair %s does not have a settlement price!" % m) + + quote, base = m.split(self.config.market_separator) + if quote in amounts : + if"symmetric_sides" in self.settings and self.settings["symmetric_sides"]: + self.sell(m, sell_price, min([amounts[quote], amounts[base] * buy_price])) + else : + self.sell(m, sell_price, amounts[quote]) + if base in amounts : + if"symmetric_sides" in self.settings and self.settings["symmetric_sides"]: + self.buy(m, buy_price, min([amounts[quote], amounts[base] * buy_price])) + else : + self.buy(m, buy_price, amounts[base] * buy_price) + +class MakerRamp(BaseStrategy): + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + def tick(self) : + self.cancel_mine() + self.place() + + def place(self) : + print("Placing Orders:") + #: Amount of Funds available for trading (per asset) + if "ramp_mode" not in self.settings: + mode = "linear" + else : + mode = self.settings["ramp_mode"] + target_price = self.settings["target_price"] + + balances = self.dex.returnBalances() + asset_ids = [] + amounts = {} + for market in self.settings["markets"]: + quote, base = market.split(self.config.market_separator) + asset_ids.append(base) + asset_ids.append(quote) + assets_unique = list(set(asset_ids)) + for a in assets_unique: + if a in balances : + amounts[a] = balances[a] * self.settings["volume_percentage"] / 100 / asset_ids.count(a) + + ticker = self.dex.returnTicker() + for m in self.settings["markets"]: + + quote, base = m.split(self.config.market_separator) + if isinstance(target_price, float): + price_target = 1.0 + elif (isinstance(target_price, str) and + target_price is "settlement_price" or + target_price is "feed" or + target_price is "price_feed"): + if "settlement_price" in ticker[m] : + price_target = ticker[m]["settlement_price"] + else : + raise Exception("Pair %s does not have a settlement price!" % m) + + if quote in amounts : + price_start = price_target * (1 + self.settings["spread_percentage"] / 200.0) + price_end = price_target * (1 + self.settings["ramp_price_percentage"] / 100.0) + amount = min([amounts[quote], amounts[base] * (price_start) / 2.0]) + number_orders = math.floor((self.settings["ramp_price_percentage"] / 100.0 - self.settings["spread_percentage"] / 200.0) / (self.settings["ramp_step_percentage"] / 100.0)) + if mode == "linear" : + for price in linspace(price_start, price_end, number_orders) : + self.sell(m, price, amount / number_orders) + elif mode == "exponential" : + k = linspace(1 / number_orders, 1, number_orders) + k = [v / sum(k) for v in k] + order_amounts = [v * amount for v in k] + for i, price in enumerate(linspace(price_start, price_end, number_orders)): + self.sell(m, price, order_amounts[i]) + else : + raise Exception("ramp_mode '%s' not known" % mode) + + if base in amounts : + price_start = price_target * (1 - self.settings["spread_percentage"] / 200.0) + price_end = price_target * (1 - self.settings["ramp_price_percentage"] / 100.0) + amount = min([amounts[quote], amounts[base] * (price_start) / 2.0]) + number_orders = math.floor((self.settings["ramp_price_percentage"] / 100.0 - self.settings["spread_percentage"] / 200.0) / (self.settings["ramp_step_percentage"] / 100.0)) + if mode == "linear" : + for price in linspace(price_start, price_end, number_orders) : + self.buy(m, price, amount / number_orders) + elif mode == "exponential" : + k = linspace(1 / number_orders, 1, number_orders) + k = [v / sum(k) for v in k] + order_amounts = [v * amount for v in k] + for i, price in enumerate(linspace(price_start, price_end, number_orders)): + self.buy(m, price, order_amounts[i]) + else : + raise Exception("ramp_mode '%s' not known" % mode) diff --git a/scripts/exchange-bots/data_MakerRamp.json b/scripts/exchange-bots/data_MakerRamp.json new file mode 100644 index 00000000..3434d388 --- /dev/null +++ b/scripts/exchange-bots/data_MakerRamp.json @@ -0,0 +1 @@ +{"orders": ["1.7.1064", "1.7.1065", "1.7.1066", "1.7.1067", "1.7.1068", "1.7.1069", "1.7.1070", "1.7.1071", "1.7.1072", "1.7.1073", "1.7.1074", "1.7.1075", "1.7.1076", "1.7.1077", "1.7.1078", "1.7.1079", "1.7.1080", "1.7.1081"]} \ No newline at end of file diff --git a/scripts/exchange-bots/data_MakerRamp2.json b/scripts/exchange-bots/data_MakerRamp2.json new file mode 100644 index 00000000..c5b65f07 --- /dev/null +++ b/scripts/exchange-bots/data_MakerRamp2.json @@ -0,0 +1 @@ +{"orders": ["1.7.1082", "1.7.1083", "1.7.1084", "1.7.1085", "1.7.1086", "1.7.1087", "1.7.1088", "1.7.1089", "1.7.1090", "1.7.1091", "1.7.1092", "1.7.1093", "1.7.1094", "1.7.1095", "1.7.1096", "1.7.1097", "1.7.1098", "1.7.1099", "1.7.1064", "1.7.1065", "1.7.1066", "1.7.1067", "1.7.1068", "1.7.1069", "1.7.1070", "1.7.1071", "1.7.1072", "1.7.1073", "1.7.1074", "1.7.1075", "1.7.1076", "1.7.1077", "1.7.1078", "1.7.1079", "1.7.1080", "1.7.1081"]} \ No newline at end of file diff --git a/scripts/exchange-bots/main.py b/scripts/exchange-bots/main.py index 3f053e71..54650983 100644 --- a/scripts/exchange-bots/main.py +++ b/scripts/exchange-bots/main.py @@ -1,39 +1,6 @@ -from bot.strategies.bridgemaker import BridgeMaker from bot import Bot - - -class Config(): - wallet_host = "localhost" - wallet_port = 8092 - wallet_user = "" - wallet_password = "" - witness_url = "ws://10.0.0.16:8090/" - witness_user = "" - witness_password = "" - watch_markets = ["BTC : TRADE.BTC", - "BTC : OPENBTC", - "OPENBTC : TRADE.BTC", - "OPENDASH : TRADE.DASH", - "OPENDOGE : TRADE.DOGE", - "OPENLTC : TRADE.LTC", - "OPENMUSE : TRADE.MUSE", - "OPENNBT : TRADE.NBT", - "OPENPPC : TRADE.PPC", - "OPENUSD : USD", - ] - market_separator = " : " - - bots = {} - bots["BridgeMaker"] = {"bot" : BridgeMaker, - "markets" : ["BTC : TRADE.BTC", - "BTC : OPENBTC"], - "spread_percentage" : 5, - "volume_percentage" : 50, - } - account = "btc-bridge-maker" +import config if __name__ == '__main__': - bot = Bot(Config) - bot.bots["BridgeMaker"].save_orders({'ref_block_num': 4383, 'expiration': '2016-01-13T13:47:57', 'extensions': [], 'signatures': ['1f53fd4e4717218ac0ef47c6fa144b48378a7ad234e89cb97aed514f16acc042d631458748aee5a5ed86467b0580661c614769fd75207831285dab56d4130aa58f'], 'operations': [[1, {'extensions': [], 'seller': '1.2.100237', 'fill_or_kill': False, 'fee': {'amount': 1000000, 'asset_id': '1.3.0'}, 'min_to_receive': {'amount': 58479, 'asset_id': '1.3.103'}, 'expiration': '2016-01-20T13:47:29', 'amount_to_sell': {'amount': 57017, 'asset_id': '1.3.569'}}]], 'ref_block_prefix': 3665477119}) - bot.bots["BridgeMaker"].save_orders({'ref_block_num': 4383, 'expiration': '2016-01-13T13:47:57', 'extensions': [], 'signatures': ['20285813f0fcab111a7d790d0ec2ad44add31dc9510a593248bd84e8b3abe347295cc51f7c8cb1851933b5229c3e8ef5eaadb3eb1101a52f028c70bf7c082595d4'], 'operations': [[1, {'extensions': [], 'seller': '1.2.100237', 'fill_or_kill': False, 'fee': {'amount': 1000000, 'asset_id': '1.3.0'}, 'min_to_receive': {'amount': 61478, 'asset_id': '1.3.350'}, 'expiration': '2016-01-20T13:47:29', 'amount_to_sell': {'amount': 59979, 'asset_id': '1.3.569'}}]], 'ref_block_prefix': 3665477119}) + bot = Bot(config) bot.execute()