def smsalert(message): # Send message via SMS. snsresponse = snsclient.publish(PhoneNumber='+15108045618', Message=message) responseout = json.dumps(snsresponse, sort_keys=True, indent=4, separators=(',', ': ')) logger.debug(f'Response to SNS Request:\n{responseout}')
def quotabid(pair: str, cash: str, cost: str) -> None: # Determine API transaction fee. # Refer to https://docs.gemini.com/rest-api/#basis-point. # Fees are calculated on the notional value of each trade (price × size). # Meaning (for API transactions): size * price * 1.001 = cash fraction = Decimal(constants.apitransactionfee) notional = Decimal(cash) / Decimal(1 + fraction) # Determine minimum order size (let's call it a tock). list = constants.minimumorders item = [ item['minimumorder'] for item in list if item['currency'] == pair[:3] ] tock = Decimal(item[0]) # Determine bid size. quantity = str(Decimal(notional / Decimal(cost)).quantize(tock)) bidprice = str(cost) # Update logs. logger.debug(f'bidprice: {bidprice}') logger.debug(f'quantity: {quantity}') # Construct buy order payload. # Use 'options': ['maker-or-cancel'] for post only orders. endpoint = '/v1/order/new' t = datetime.datetime.now() payload = { 'request': endpoint, 'nonce': str(int(time.mktime(t.timetuple()) * 1000)), 'symbol': pair, 'amount': quantity, 'price': bidprice, 'side': 'buy', 'type': 'exchange limit', 'options': ['maker-or-cancel'] } headers = authenticator.authenticate(payload) request = resourcelocator.restserver + endpoint response = requests.post(request, data=None, headers=headers['restheader']) return response
def on_message(ws, message, drop=drop, pair=pair, high=high, deal=deal): dictionary = json.loads(message) percentoff = Decimal(drop) sessionmax = Decimal(high.getvalue()) # Uncomment this statement to debug messages: logger.debug(dictionary) # Process "type": "update" messages with events only. if 'update' in dictionary['type']: if dictionary['events'] != []: events = dictionary['events'] # Process "type": "trade" events for latest price. for event in events: if event['type'] == 'trade': last = Decimal(event["price"]) if last.compare(Decimal(sessionmax)) == 1: sessionmax = last high.setvalue(last) # Calculate movement away from high [if any]. move = 100 * (sessionmax - last) / sessionmax # Display impact of event information received. logger.info( f'{move:.2f}% off highs [{sessionmax}] : {pair} is {last} presently : [Message ID: {dictionary["socket_sequence"]}].' ) # Define bargain (sale) price. sale = Decimal(sessionmax * (1 - percentoff)) # Exit loop if there's a sale. if sale.compare(last) == 1: logger.info( f'{pair} [now {last:.2f}] just went on sale [dropped below {sale:.2f}].' ) smsalert( f'There was a {percentoff*100}% drop in the price of the {pair} pair on Gemini.' ) # Update deal price. deal.setvalue(last) ws.close()
def quotabid( pair: str, cash: str, ) -> None: # Determine API transaction fee. # Refer to https://docs.gemini.com/rest-api/#basis-point. # Fees are calculated on the notional value of each trade (price × size). # Meaning (for API transactions): size * price * 1.001 = cash fraction = Decimal( constants.apitransactionfee ) notional = Decimal(cash) / Decimal( 1 + fraction ) # Determine tick size. list = constants.ticksizes item = [ item['tick'] for item in list if item['currency'] == pair[:3] ] tick = Decimal( item[0] ) # Determine quantity size. list = constants.minimumquantities item = [ item['minimumquantity'] for item in list if item['currency'] == pair[:3] ] bump = Decimal( item[0] ) # Get the highest bid in the orderbook. # Make an offer that's one tick better. # Then determine the bid order size. endpoint = '/v1/pubticker/' + pair response = requests.get( resourcelocator.restserver + endpoint ) bidprice = Decimal( response.json()['bid'] ) offering = str( Decimal( bidprice + tick ).quantize( tick ) ) quantity = str( Decimal( notional / Decimal(offering) ).quantize( bump ) ) # Update logs. logger.debug(f'bidprice: {bidprice}') logger.debug(f'offering: {offering}') logger.debug(f'quantity: {quantity}') # Construct buy order payload. # Use 'options': ['maker-or-cancel'] for post only orders. endpoint = '/v1/order/new' t = datetime.datetime.now() payload = { 'request': endpoint, 'nonce': str(int(time.mktime(t.timetuple())*1000)), 'symbol': pair, 'amount': quantity, 'price': offering, 'side': 'buy', 'type': 'exchange limit', 'options': ['maker-or-cancel'] } headers = authenticator.authenticate(payload) request = resourcelocator.restserver + endpoint response = requests.post(request, data = None, headers = headers['restheader']) return response
def bidorder( pair: str, size: str, ) -> None: # Determine tick size. list = constants.ticksizes item = [ item['tick'] for item in list if item['currency'] == pair[:3] ] tick = Decimal( item[0] ) # Determine quantity size. list = constants.minimumquantities item = [ item['minimumquantity'] for item in list if item['currency'] == pair[:3] ] bump = Decimal( item[0] ) # Get the highest bid in the orderbook. # Make an offer that's one tick better. endpoint = '/v1/pubticker/' + pair response = requests.get( resourcelocator.restserver + endpoint ) bidprice = Decimal( response.json()['bid'] ) offering = str( Decimal( bidprice + tick ).quantize( tick ) ) quantity = str( Decimal( size ).quantize( bump ) ) # Update logs. logger.debug(f'bidprice: {bidprice}') logger.debug(f'offering: {offering}') logger.debug(f'quantity: {quantity}') # Construct buy order payload. # Use 'options': ['maker-or-cancel'] for post only orders. endpoint = '/v1/order/new' t = datetime.datetime.now() payload = { 'request': endpoint, 'nonce': str(int(time.mktime(t.timetuple())*1000)), 'symbol': pair, 'amount': quantity, 'price': offering, 'side': 'buy', 'type': 'exchange limit', 'options': ['maker-or-cancel'] } headers = authenticator.authenticate(payload) request = resourcelocator.restserver + endpoint response = requests.post(request, data = None, headers = headers['restheader']) return response
# Override defaults with command line parameters. if len(sys.argv) == 4: pair = sys.argv[1] size = sys.argv[2] rise = sys.argv[3] # Open websocket connection. # Wait for bids to rise in price. logger.info( f'waiting for {pair} to rise {Decimal(rise)*100}% in price to sell {size} {pair[3:]} worth..' ) deal = bidrise(pair, rise) if deal: # Submit limit ask order. logger.debug(f'submitting {pair} frontrunning limit ask order.') post = askorder(pair, size) post = post.json() dump = json.dumps(post, sort_keys=True, indent=4, separators=(',', ': ')) logger.debug(dump) # Define poststatus class. # Purpose: Stores the state of the orderid parameter upon exiting the websocket connection session. class Poststatus: def __init__(self, state): self.__state = state def getvalue(self): return self.__state def setvalue(self, state):
# Set the pair. # Set quote currency (USD in this case) budget. # Set your price reservation. pair = 'DAIUSD' cash = '100' cost = '1.00913' # Override defaults with command line parameters from BASH wrapper. if len(sys.argv) == 4: pair = sys.argv[1] cash = sys.argv[2] cost = sys.argv[3] # Submit limit ask order. logger.debug( f'submitting {pair} limit ask order: {cost} ask on a {cost} budget.') post = quotaask(pair, cash, cost) post = post.json() dump = json.dumps(post, sort_keys=True, indent=4, separators=(',', ': ')) logger.debug(dump) # Define poststatus class. # Purpose: Stores the state of the orderid parameter upon exiting the websocket connection session. class Poststatus: def __init__(self, state): self.__state = state def getvalue(self): return self.__state
if len(sys.argv) == 5: pair = sys.argv[1] size = sys.argv[2] drop = sys.argv[3] rise = sys.argv[4] # Open websocket connection. # Wait for asks to fall in price. logger.info( f'waiting for {pair} to drop {Decimal(drop)*100}% in price to buy {size} {pair[:3]}..' ) deal = askfall(pair, drop) if deal: # Submit limit bid order. logger.debug(f'submitting {pair} frontrunning limit bid order.') post = bidorder(pair, size) post = post.json() dump = json.dumps(post, sort_keys=True, indent=4, separators=(',', ': ')) logger.debug(dump) # Define poststatus class. # Purpose: Stores the state of the orderid parameter upon exiting the websocket connection session. class Poststatus: def __init__(self, state): self.__state = state def getvalue(self): return self.__state def setvalue(self, state):
from libraries.logger import logger from libraries.spreadkiller import askorder from libraries.fillvalidator import confirmexecution # Set bid size ['0.1' is the minimum for DAIUSD]. pair = 'DAIUSD' size = '0.1' # Override defaults with command line parameters. if len(sys.argv) == 3: pair = sys.argv[1] size = sys.argv[2] # Submit limit bid order. logger.debug(f'submitting {pair} aggressive limit bid order.') post = askorder( pair, size ) post = post.json() dump = json.dumps( post, sort_keys=True, indent=4, separators=(',', ': ') ) logger.debug ( dump ) # Define poststatus class. # Purpose: Stores the state of the orderid parameter upon exiting the websocket connection session. class Poststatus: def __init__(self, state): self.__state = state def getvalue(self): return self.__state def setvalue(self, state): self.__state = state poststatus = Poststatus('') # Determine if the order was filled.
def on_error(ws, error): logger.debug(error)
def on_open(ws): logger.debug(f'{ws} connection opened.')
def on_close(ws): logger.debug(f'{ws} connection closed.')
def confirmexecution(orderid: str, poststatus: object) -> None: # Introduce function. logger.debug( f'Confirming execution of the order identified by the Gemini assigned number: {orderid}' ) # Define websocket functions. def on_close(ws): logger.debug(f'{ws} connection closed.') def on_open(ws): logger.debug(f'{ws} connection opened.') def on_error(ws, error): logger.debug(error) def on_message(ws, message, orderid=orderid): dictionary = json.loads(message) exitstatus = '' # Remove comment to debug with: logger.debug( dictionary ) # Check if this is an 'initial' message from Gemini. # It is actually the second message. The subscription acknowledgement is first. if dictionary == []: exitstatus = f'Order {orderid} not active.' if isinstance(dictionary, list): for listitem in dictionary: size = listitem['original_amount'] pair = listitem['symbol'].upper() rate = listitem['price'] side = listitem['side'] cost = Decimal(size) * Decimal(rate) bit0 = f'{pair} {side} order {orderid} valued at ' bit1 = f'{cost.quantize( Decimal(rate) )} {pair[3:].upper()} ' bit2 = f'[{size} {pair[:3].upper()} at {rate} {pair[3:].upper()}] was ' text = f'{bit0}{bit1}{bit2}' if listitem['order_id'] == orderid: # Exit upon receiving order cancellation message. if listitem['is_cancelled']: exitstatus = f'{text} cancelled.' if listitem['type'] == 'cancelled': exitstatus = f'{text} cancelled [reason:{listitem["reason"]}].' if listitem['type'] == 'rejected': exitstatus = f'{text} rejected.' if listitem['type'] == 'fill': # Make sure that the order was completely filled. if listitem['remaining_amount'] == '0': exitstatus = f'{text} filled.' if exitstatus: ws.close() logger.info(exitstatus) smsalert(exitstatus) poststatus.setvalue(exitstatus) # Construct payload. endpoint = '/v1/order/events' nonce = int(time.time() * 1000) payload = {'request': endpoint, 'nonce': nonce} header = authenticator.authenticate(payload) # Establish websocket connection. logger.debug( f'Establishing websocket connection to confirm the execution of order number {orderid}.' ) ws = websocket.WebSocketApp(str(resourcelocator.sockserver + endpoint), on_open=on_open, on_message=on_message, on_error=on_error, on_close=on_close, header=header['sockheader']) ws.run_forever(sslopt={'cert_reqs': ssl.CERT_NONE})
import requests import locale from libraries.logger import logger from libraries.volumizer import notionalvolume # Set default field value. field = '' # Override defaults with command line parameters. if len(sys.argv) == 2: field = sys.argv[1] # Submit request. logger.debug(f'submitting request...') post = notionalvolume() data = post.json() dump = json.dumps( data, sort_keys=True, indent=4, separators=(',', ': ') ) # Format response. if field: if field == 'notional_30d_volume': print( f'your notional 30-day volume is {post.json()["notional_30d_volume"]:,} USD.' ) if field == 'api_maker_fee_bps': print( f'the fee that Gemini is charging you for making orders via the API is {post.json()["api_maker_fee_bps"]} basis points.' ) logger.debug ( dump ) # Let the shell know we successfully made it this far! if post: sys.exit(0)
if len(sys.argv) == 5: pair = sys.argv[1] cash = sys.argv[2] drop = sys.argv[3] rise = sys.argv[4] # Open websocket connection. # Wait for asks to fall in price. logger.info( f'waiting for {pair} to drop {Decimal(drop)*100}% in price to buy {cash} {pair[3:]} worth..' ) deal = askfall(pair, drop) if deal: # Submit limit bid order. logger.debug(f'submitting {pair} frontrunning limit bid order.') post = quotabid(pair, cash) post = post.json() dump = json.dumps(post, sort_keys=True, indent=4, separators=(',', ': ')) logger.debug(dump) # Define poststatus class. # Purpose: Stores the state of the orderid parameter upon exiting the websocket connection session. class Poststatus: def __init__(self, state): self.__state = state def getvalue(self): return self.__state def setvalue(self, state):