Skip to content

Commit

Permalink
Merge branch 'feature/propose-trades' into develop
Browse files Browse the repository at this point in the history
  • Loading branch information
xeroc committed Mar 10, 2016
2 parents b03cea4 + 71d5451 commit 61bd4d7
Show file tree
Hide file tree
Showing 3 changed files with 145 additions and 37 deletions.
124 changes: 88 additions & 36 deletions grapheneexchange/exchange.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from datetime import datetime
import time
import math
from grapheneextra.proposal import Proposal


class ExampleConfig() :
Expand Down Expand Up @@ -120,8 +121,20 @@ class Config():
account = ""
wallet = None

def __init__(self, config, safe_mode=True) :
self.safe_mode = safe_mode
def __init__(self, config, **kwargs) :
# Defaults:
self.safe_mode = True

#: Propose transactions (instead of broadcasting every order, we
# here propose every order in a single proposal
self.propose_only = False
self.propose_operations = []

if "safe_mode" in kwargs:
self.safe_mode = kwargs["safe_mode"]
if "propose_only" in kwargs:
self.propose_only = kwargs["propose_only"]

self.config = config
super().__init__(config)

Expand Down Expand Up @@ -733,7 +746,7 @@ def returnTradeHistory(self, currencyPair="all", limit=25):
r.update({market : trades})
return r

def buy(self, currencyPair, rate, amount):
def buy(self, currencyPair, rate, amount, expiration=7 * 24 * 60 * 60, killfill=False):
""" Places a buy order in a given market (buy ``quote``, sell
``base`` in market ``quote_base``). Required POST parameters
are "currencyPair", "rate", and "amount". If successful, the
Expand All @@ -742,6 +755,8 @@ def buy(self, currencyPair, rate, amount):
:param str currencyPair: Return results for a particular market only (default: "all")
:param float price: price denoted in ``base``/``quote``
:param number amount: Amount of ``quote`` to buy
:param number expiration: (optional) expiration time of the order in seconds (defaults to 7 days)
:param bool killfill: flag that indicates if the order shall be killed if it is not filled (defaults to False)
Prices/Rates are denoted in 'base', i.e. the USD_BTS market
is priced in BTS per USD.
Expand All @@ -762,17 +777,21 @@ def buy(self, currencyPair, rate, amount):
quote_symbol, base_symbol = currencyPair.split(self.market_separator)
base = self.rpc.get_asset(base_symbol)
quote = self.rpc.get_asset(quote_symbol)
# Check amount > 0
return self.rpc.sell_asset(self.config.account,
'{:.{prec}f}'.format(amount * rate, prec=base["precision"]),
base_symbol,
'{:.{prec}f}'.format(amount, prec=quote["precision"]),
quote_symbol,
7 * 24 * 60 * 60,
False,
not self.safe_mode)

def sell(self, currencyPair, rate, amount):
transaction = self.rpc.sell_asset(self.config.account,
'{:.{prec}f}'.format(amount * rate, prec=base["precision"]),
base_symbol,
'{:.{prec}f}'.format(amount, prec=quote["precision"]),
quote_symbol,
expiration,
killfill,
not (self.safe_mode or self.propose_only))
if self.propose_only:
[self.propose_operations.append(o) for o in transaction["operations"]]
return self.propose_operations
else:
return transaction

def sell(self, currencyPair, rate, amount, expiration=7 * 24 * 60 * 60, killfill=False):
""" Places a sell order in a given market (sell ``quote``, buy
``base`` in market ``quote_base``). Required POST parameters
are "currencyPair", "rate", and "amount". If successful, the
Expand All @@ -781,6 +800,8 @@ def sell(self, currencyPair, rate, amount):
:param str currencyPair: Return results for a particular market only (default: "all")
:param float price: price denoted in ``base``/``quote``
:param number amount: Amount of ``quote`` to sell
:param number expiration: (optional) expiration time of the order in seconds (defaults to 7 days)
:param bool killfill: flag that indicates if the order shall be killed if it is not filled (defaults to False)
Prices/Rates are denoted in 'base', i.e. the USD_BTS market
is priced in BTS per USD.
Expand All @@ -801,14 +822,19 @@ def sell(self, currencyPair, rate, amount):
quote_symbol, base_symbol = currencyPair.split(self.market_separator)
base = self.rpc.get_asset(base_symbol)
quote = self.rpc.get_asset(quote_symbol)
return self.rpc.sell_asset(self.config.account,
'{:.{prec}f}'.format(amount, prec=quote["precision"]),
quote_symbol,
'{:.{prec}f}'.format(amount * rate, prec=base["precision"]),
base_symbol,
7 * 24 * 60 * 60,
False,
not self.safe_mode)
transaction = self.rpc.sell_asset(self.config.account,
'{:.{prec}f}'.format(amount, prec=quote["precision"]),
quote_symbol,
'{:.{prec}f}'.format(amount * rate, prec=base["precision"]),
base_symbol,
expiration,
killfill,
not (self.safe_mode or self.propose_only))
if self.propose_only:
[self.propose_operations.append(o) for o in transaction["operations"]]
return self.propose_operations
else:
return transaction

def close_debt_position(self, symbol):
""" Close a debt position and reclaim the collateral
Expand Down Expand Up @@ -919,11 +945,16 @@ def borrow(self, amount, symbol, collateral_ratio):
(fundsNeeded, collateral_asset["symbol"], fundsHave, collateral_asset["symbol"]))

# Borrow
return self.rpc.borrow_asset(self.config.account,
'{:.{prec}f}'.format(amount, prec=asset["precision"]),
symbol,
'{:.{prec}f}'.format(amount_of_collateral, prec=collateral_asset["precision"]),
not self.safe_mode)
transaction = self.rpc.borrow_asset(self.config.account,
'{:.{prec}f}'.format(amount, prec=asset["precision"]),
symbol,
'{:.{prec}f}'.format(amount_of_collateral, prec=collateral_asset["precision"]),
not (self.safe_mode or self.propose_only))
if self.propose_only:
[self.propose_operations.append(o) for o in transaction["operations"]]
return self.propose_operations
else:
return transaction

def cancel(self, orderNumber):
""" Cancels an order you have placed in a given market. Requires
Expand All @@ -935,16 +966,13 @@ def cancel(self, orderNumber):
if self.safe_mode :
print("Safe Mode enabled!")
print("Please GrapheneExchange(config, safe_mode=False) to remove this and execute the transaction below")
# return self.rpc.cancel_order(orderNumber, not self.safe_mode)
transaction = self.rpc.cancel_order(orderNumber, not (self.safe_mode or self.propose_only))

account = self.rpc.get_account(self.config.account)
op = self.rpc.get_prototype_operation("limit_order_cancel_operation")
op[1]["fee_paying_account"] = account["id"]
op[1]["order"] = orderNumber
buildHandle = self.rpc.begin_builder_transaction()
self.rpc.add_operation_to_builder_transaction(buildHandle, op)
self.rpc.set_fees_on_builder_transaction(buildHandle, "1.3.0")
return self.rpc.sign_builder_transaction(buildHandle, True)
if self.propose_only:
[self.propose_operations.append(o) for o in transaction["operations"]]
return self.propose_operations
else:
return transaction

def withdraw(self, currency, amount, address):
""" This Method makes no sense in a decentralized exchange
Expand Down Expand Up @@ -1134,3 +1162,27 @@ def cancel_asks_out_of_range(self, market, price, tolerance):
self.cancel(order["orderNumber"])
canceledOrders.append(order["orderNumber"])
return canceledOrders

def propose_all(self, expiration=None, proposer=None):
""" If ``proposal_only`` is set True, this method needs to be
called to **actuctually** propose the operations on the
chain.
:param time expiration: expiration time formated as ``%Y-%m-%dT%H:%M:%S`` (defaults to 24h)
:param string proposer: name of the account that pays the proposer fee
"""
if not proposer:
proposer = self.config.account
if not expiration:
expiration = datetime.utcfromtimestamp(time.time() + 60 * 60 * 24).strftime('%Y-%m-%dT%H:%M:%S')
account = self.rpc.get_account(proposer)
proposal = Proposal(self)
return proposal.propose_operations(self.propose_operations,
expiration,
account["id"],
broadcast=not self.safe_mode)

def proposals_clear(self):
""" Clear stored proposals
"""
self.propose_operations = []
55 changes: 55 additions & 0 deletions grapheneextra/proposal.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
from datetime import datetime
import time

# from graphenebase.transactions import operations


class Proposal(object) :
""" Manage Proposals
Expand Down Expand Up @@ -70,3 +72,56 @@ def propose_transfer(self, proposer_account, from_account, to_account,
self.client.rpc.propose_builder_transaction2(buildHandle, proposer["name"], exp_time, 0, False)
self.client.rpc.set_fees_on_builder_transaction(buildHandle, asset["id"])
return self.client.rpc.sign_builder_transaction(buildHandle, broadcast)

def propose_operations(self, ops, expiration, proposer_account, preview=0, broadcast=False):
""" Propose several operations
:param Array ops: Array of operations
:param time expiration: Expiration time in format '%Y-%m-%dT%H:%M:%S'
:param proposer_account: Account name or id of the proposer (pays the proposal fee)
:param number preview: Preview period (in seconds)
:param bool broadcast: If true, broadcasts the transaction
:return: Signed transaction
:rtype: json
Once a proposal has been signed, the corresponding
transaction hash can be obtained via:
.. code-block:: python
print(rpc.get_transaction_id(tx))
"""

proposer = self.client.rpc.get_account(proposer_account)
buildHandle = self.client.rpc.begin_builder_transaction()
for op in ops:
self.client.rpc.add_operation_to_builder_transaction(buildHandle, op)
self.client.rpc.set_fees_on_builder_transaction(buildHandle, "1.3.0")
self.client.rpc.propose_builder_transaction2(buildHandle, proposer["name"], expiration, preview, False)
self.client.rpc.set_fees_on_builder_transaction(buildHandle, "1.3.0")
return self.client.rpc.sign_builder_transaction(buildHandle, broadcast)

# ## Alternative implementation building the transactions
# ## manually. Not yet working though
# op = self.client.rpc.get_prototype_operation("proposal_create_operation")
# for o in ops :
# op[1]["proposed_ops"].append(o)
# op[1]["expiration_time"] = expiration
# op[1]["fee_paying_account"] = payee_id
# op[1]["fee"] = self.get_operations_fee(op, "1.3.0")
# buildHandle = self.client.rpc.begin_builder_transaction()
# from pprint import pprint
# pprint(op)
# self.client.rpc.add_operation_to_builder_transaction(buildHandle, op)
# # print(self.client.rpc.preview_builder_transaction(buildHandle))
# return self.client.rpc.sign_builder_transaction(buildHandle, broadcast)

# def get_operations_fee(self, op, asset_id):
# global_parameters = self.client.rpc.get_object("2.0.0")[0]["parameters"]["current_fees"]
# parameters = global_parameters["parameters"]
# scale = global_parameters["scale"] / 1e4
# opID = op[0]
# assert asset_id == "1.3.0", "asset_id has to be '1.3.0'"
# # FIXME limition to "fee"-only! Need to evaluate every other as well
# return {"amount": parameters[opID][1]["fee"],
# "asset_id": asset_id}
3 changes: 2 additions & 1 deletion scripts/exchange-simpleticker-stats/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ class Config():
wallet_port = 8092
wallet_user = ""
wallet_password = ""
witness_url = "ws://10.0.0.16:8090/"
witness_url = "wss://bitshares.openledger.info/ws/"
# witness_url = "ws://10.0.0.16:8090/"
# witness_url = "ws://testnet.bitshares.eu/ws"
witness_user = ""
witness_password = ""
Expand Down

0 comments on commit 61bd4d7

Please sign in to comment.