mirror of
https://github.com/basicswap/basicswap.git
synced 2025-11-05 10:28:10 +01:00
tests: Add script test
This commit is contained in:
@@ -9,10 +9,13 @@
|
||||
Create offers
|
||||
"""
|
||||
|
||||
__version__ = '0.1'
|
||||
__version__ = '0.2'
|
||||
|
||||
import os
|
||||
import json
|
||||
import time
|
||||
import random
|
||||
import shutil
|
||||
import signal
|
||||
import urllib
|
||||
import logging
|
||||
@@ -22,23 +25,35 @@ from urllib.request import urlopen
|
||||
|
||||
delay_event = threading.Event()
|
||||
|
||||
|
||||
def post_json_req(url, json_data):
|
||||
req = urllib.request.Request(url)
|
||||
req.add_header('Content-Type', 'application/json; charset=utf-8')
|
||||
post_bytes = json.dumps(json_data).encode('utf-8')
|
||||
req.add_header('Content-Length', len(post_bytes))
|
||||
return urlopen(req, post_bytes, timeout=300).read()
|
||||
DEFAULT_CONFIG_FILE: str = 'createoffers.json'
|
||||
DEFAULT_STATE_FILE: str = 'createoffers_state.json'
|
||||
|
||||
|
||||
def read_json_api(port, path=None, json_data=None):
|
||||
url = f'http://127.0.0.1:{port}/json'
|
||||
if path is not None:
|
||||
url += '/' + path
|
||||
def post_req(url: str, json_data=None):
|
||||
req = urllib.request.Request(url, headers={'User-Agent': 'Mozilla/5.0'})
|
||||
if json_data:
|
||||
req.add_header('Content-Type', 'application/json; charset=utf-8')
|
||||
post_bytes = json.dumps(json_data).encode('utf-8')
|
||||
req.add_header('Content-Length', len(post_bytes))
|
||||
else:
|
||||
post_bytes = None
|
||||
return urlopen(req, data=post_bytes, timeout=300).read()
|
||||
|
||||
if json_data is not None:
|
||||
return json.loads(post_json_req(url, json_data))
|
||||
return json.loads(urlopen(url, timeout=300).read())
|
||||
|
||||
def make_json_api_func(host: str, port: int):
|
||||
host = host
|
||||
port = port
|
||||
|
||||
def api_func(path=None, json_data=None, timeout=300):
|
||||
nonlocal host, port
|
||||
url = f'http://{host}:{port}/json'
|
||||
if path is not None:
|
||||
url += '/' + path
|
||||
if json_data is not None:
|
||||
return json.loads(post_req(url, json_data))
|
||||
response = urlopen(url, timeout=300).read()
|
||||
return json.loads(response)
|
||||
return api_func
|
||||
|
||||
|
||||
def signal_handler(sig, frame) -> None:
|
||||
@@ -55,74 +70,208 @@ def findCoin(coin: str, known_coins) -> str:
|
||||
raise ValueError(f'Unknown coin {coin}')
|
||||
|
||||
|
||||
def readTemplates(known_coins):
|
||||
offer_templates = []
|
||||
with open('offer_rules.csv', 'r') as fp:
|
||||
for i, line in enumerate(fp):
|
||||
if i < 1:
|
||||
continue
|
||||
line = line.strip()
|
||||
if line[0] == '#':
|
||||
continue
|
||||
row_data = line.split(',')
|
||||
try:
|
||||
if len(row_data) < 6:
|
||||
raise ValueError('missing data')
|
||||
offer_template = {}
|
||||
offer_template['coin_from'] = findCoin(row_data[0], known_coins)
|
||||
offer_template['coin_to'] = findCoin(row_data[1], known_coins)
|
||||
offer_template['amount'] = row_data[2]
|
||||
offer_template['minrate'] = float(row_data[3])
|
||||
offer_template['ratetweakpercent'] = float(row_data[4])
|
||||
offer_template['amount_variable'] = row_data[5].lower() in ('true', 1)
|
||||
offer_template['address'] = row_data[6]
|
||||
offer_templates.append(offer_template)
|
||||
except Exception as e:
|
||||
print(f'Warning: Skipping row {i}, {e}')
|
||||
continue
|
||||
return offer_templates
|
||||
def readConfig(args, known_coins):
|
||||
config_path: str = args.configfile
|
||||
num_changes: int = 0
|
||||
with open(config_path) as fs:
|
||||
config = json.load(fs)
|
||||
|
||||
if not 'offers' in config:
|
||||
config['offers'] = []
|
||||
if not 'bids' in config:
|
||||
config['bids'] = []
|
||||
if not 'stealthex' in config:
|
||||
config['stealthex'] = []
|
||||
|
||||
if not 'min_seconds_between_offers' in config:
|
||||
config['min_seconds_between_offers'] = 60
|
||||
print('Set min_seconds_between_offers', config['min_seconds_between_offers'])
|
||||
num_changes += 1
|
||||
if not 'max_seconds_between_offers' in config:
|
||||
config['max_seconds_between_offers'] = config['min_seconds_between_offers'] * 4
|
||||
print('Set max_seconds_between_offers', config['max_seconds_between_offers'])
|
||||
num_changes += 1
|
||||
|
||||
if not 'min_seconds_between_bids' in config:
|
||||
config['min_seconds_between_bids'] = 60
|
||||
print('Set min_seconds_between_bids', config['min_seconds_between_bids'])
|
||||
num_changes += 1
|
||||
if not 'max_seconds_between_bids' in config:
|
||||
config['max_seconds_between_bids'] = config['min_seconds_between_bids'] * 4
|
||||
print('Set max_seconds_between_bids', config['max_seconds_between_bids'])
|
||||
num_changes += 1
|
||||
|
||||
offer_templates = config['offers']
|
||||
offer_templates_map = {}
|
||||
num_enabled = 0
|
||||
for i, offer_template in enumerate(offer_templates):
|
||||
num_enabled += 1 if offer_template.get('enabled', True) else 0
|
||||
if 'name' not in offer_template:
|
||||
print('naming offer template', i)
|
||||
offer_template['name'] = f'Offer {i}'
|
||||
num_changes += 1
|
||||
if offer_template.get('min_coin_from_amt', 0) < offer_template['amount']:
|
||||
print('Setting min_coin_from_amt for', offer_template['name'])
|
||||
offer_template['min_coin_from_amt'] = offer_template['amount']
|
||||
num_changes += 1
|
||||
|
||||
if offer_template.get('enabled', True) is False:
|
||||
continue
|
||||
offer_template['coin_from'] = findCoin(offer_template['coin_from'], known_coins)
|
||||
offer_template['coin_to'] = findCoin(offer_template['coin_to'], known_coins)
|
||||
|
||||
if offer_template['name'] in offer_templates_map:
|
||||
print('renaming offer template', offer_template['name'])
|
||||
original_name = offer_template['name']
|
||||
offset = 2
|
||||
while f'{original_name}_{offset}' in offer_templates_map:
|
||||
offset += 1
|
||||
offer_template['name'] = f'{original_name}_{offset}'
|
||||
num_changes += 1
|
||||
offer_templates_map[offer_template['name']] = offer_template
|
||||
config['num_enabled_offers'] = num_enabled
|
||||
|
||||
bid_templates = config['bids']
|
||||
bid_templates_map = {}
|
||||
num_enabled = 0
|
||||
for i, bid_template in enumerate(bid_templates):
|
||||
num_enabled += 1 if bid_template.get('enabled', True) else 0
|
||||
if 'name' not in bid_template:
|
||||
print('naming bid template', i)
|
||||
bid_template['name'] = f'Bid {i}'
|
||||
num_changes += 1
|
||||
|
||||
if bid_template.get('enabled', True) is False:
|
||||
continue
|
||||
|
||||
if bid_template.get('min_swap_amount', 0.0) < 0.00001:
|
||||
print('Setting min_swap_amount for bid template', bid_template['name'])
|
||||
bid_template['min_swap_amount'] = 0.00001
|
||||
|
||||
bid_template['coin_from'] = findCoin(bid_template['coin_from'], known_coins)
|
||||
bid_template['coin_to'] = findCoin(bid_template['coin_to'], known_coins)
|
||||
|
||||
if bid_template['name'] in bid_templates_map:
|
||||
print('renaming bid template', offer_templates_map_template['name'])
|
||||
original_name = bid_template['name']
|
||||
offset = 2
|
||||
while f'{original_name}_{offset}' in bid_templates_map:
|
||||
offset += 1
|
||||
bid_template['name'] = f'{original_name}_{offset}'
|
||||
num_changes += 1
|
||||
bid_templates_map[bid_template['name']] = bid_template
|
||||
config['num_enabled_bids'] = num_enabled
|
||||
|
||||
num_enabled = 0
|
||||
stealthex_swaps = config['stealthex']
|
||||
for i, swap in enumerate(stealthex_swaps):
|
||||
num_enabled += 1 if swap.get('enabled', True) else 0
|
||||
swap['coin_from'] = findCoin(swap['coin_from'], known_coins)
|
||||
#bid_template['coin_to'] = findCoin(bid_template['coin_to'], known_coins)
|
||||
config['num_enabled_swaps'] = num_enabled
|
||||
|
||||
if num_changes > 0:
|
||||
shutil.copyfile(config_path, config_path + '.last')
|
||||
with open(config_path, 'w') as fp:
|
||||
json.dump(config, fp, indent=4)
|
||||
|
||||
return config
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument('-v', '--version', action='version',
|
||||
version='%(prog)s {version}'.format(version=__version__))
|
||||
parser.add_argument('--host', dest='host', help='RPC host (default=127.0.0.1)', type=str, default='127.0.0.1', required=False)
|
||||
parser.add_argument('--port', dest='port', help='RPC port (default=12700)', type=int, default=12700, required=False)
|
||||
parser.add_argument('--oneshot', dest='oneshot', help='Exit after one iteration (default=false)', required=False, action='store_true')
|
||||
parser.add_argument('--debug', dest='debug', help='Print extra debug messages (default=false)', required=False, action='store_true')
|
||||
parser.add_argument('--configfile', dest='configfile', help=f'config file path (default={DEFAULT_CONFIG_FILE})', type=str, default=DEFAULT_CONFIG_FILE, required=False)
|
||||
parser.add_argument('--statefile', dest='statefile', help=f'state file path (default={DEFAULT_STATE_FILE})', type=str, default=DEFAULT_STATE_FILE, required=False)
|
||||
args = parser.parse_args()
|
||||
|
||||
if not os.path.exists('offer_rules.csv'):
|
||||
with open('offer_rules.csv', 'w') as fp:
|
||||
# Set address to -1 to use new addresses
|
||||
fp.write('coin from,coin to,offer value,min rate,rate tweak percent,amount variable,address')
|
||||
read_json_api = make_json_api_func(args.host, args.port)
|
||||
|
||||
known_coins = read_json_api(args.port, 'coins')
|
||||
if not os.path.exists(args.configfile):
|
||||
raise ValueError(f'Config file "{args.configfile}" not found.')
|
||||
|
||||
known_coins = read_json_api('coins')
|
||||
coins_map = {}
|
||||
for known_coin in known_coins:
|
||||
coins_map[known_coin['name']] = known_coin
|
||||
|
||||
script_state = {}
|
||||
if os.path.exists(args.statefile):
|
||||
with open(args.statefile) as fs:
|
||||
script_state = json.load(fs)
|
||||
|
||||
signal.signal(signal.SIGINT, signal_handler)
|
||||
while not delay_event.is_set():
|
||||
# Read templates each iteration so they can be modified without restarting
|
||||
offer_templates = readTemplates(known_coins)
|
||||
# Read config each iteration so they can be modified without restarting
|
||||
config = readConfig(args, known_coins)
|
||||
offer_templates = config['offers']
|
||||
random.shuffle(offer_templates)
|
||||
|
||||
bid_templates = config['bids']
|
||||
random.shuffle(bid_templates)
|
||||
|
||||
stealthex_swaps = config['stealthex']
|
||||
random.shuffle(bid_templates)
|
||||
|
||||
# override wallet api calls for testing
|
||||
if 'wallet_port_override' in config:
|
||||
wallet_api_port = int(config['wallet_port_override'])
|
||||
print(f'Overriding wallet api port: {wallet_api_port}')
|
||||
read_json_api_wallet = make_json_api_func(args.host, wallet_api_port)
|
||||
else:
|
||||
read_json_api_wallet = read_json_api
|
||||
|
||||
num_state_changes: int = 0
|
||||
try:
|
||||
recieved_offers = read_json_api(args.port, 'offers', {'active': 'active', 'include_sent': False})
|
||||
print('recieved_offers', recieved_offers)
|
||||
|
||||
sent_offers = read_json_api(args.port, 'sentoffers', {'active': 'active'})
|
||||
sent_offers = read_json_api('sentoffers', {'active': 'active'})
|
||||
|
||||
if args.debug and len(offer_templates) > 0:
|
||||
print('Processing {} offer templates'.format(config['num_enabled_offers']))
|
||||
for offer_template in offer_templates:
|
||||
offers_found = 0
|
||||
for offer in sent_offers:
|
||||
if offer['coin_from'] == offer_template['coin_from'] and offer['coin_to'] == offer_template['coin_to']:
|
||||
offers_found += 1
|
||||
|
||||
if offers_found > 0:
|
||||
continue
|
||||
coin_from_data = coins_map[offer_template['coin_from']]
|
||||
coin_to_data = coins_map[offer_template['coin_to']]
|
||||
|
||||
rates = read_json_api(args.port, 'rates', {'coin_from': coin_from_data['id'], 'coin_to': coin_to_data['id']})
|
||||
wallet_from = read_json_api_wallet('wallets/{}'.format(coin_from_data['ticker']))
|
||||
|
||||
for offer in sent_offers:
|
||||
created_offers = script_state.get('offers', {})
|
||||
prev_template_offers = created_offers.get(offer_template['name'], {})
|
||||
|
||||
if next((x for x in prev_template_offers if x['offer_id'] == offer['offer_id']), None):
|
||||
offers_found += 1
|
||||
if float(wallet_from['balance']) <= float(offer_template['min_coin_from_amt']):
|
||||
offer_id = offer['offer_id']
|
||||
print('Revoking offer {}, wallet from balance below minimum'.format(offer_id))
|
||||
result = read_json_api(f'revokeoffer/{offer_id}')
|
||||
print('revokeoffer', result)
|
||||
|
||||
if offers_found > 0:
|
||||
continue
|
||||
|
||||
if float(wallet_from['balance']) <= float(offer_template['min_coin_from_amt']):
|
||||
print('Skipping template {}, wallet from balance below minimum'.format(offer_template['name']))
|
||||
continue
|
||||
|
||||
delay_next_offer_before = script_state.get('delay_next_offer_before', 0)
|
||||
if delay_next_offer_before > int(time.time()):
|
||||
print('Delaying offers until {}'.format(delay_next_offer_before))
|
||||
break
|
||||
|
||||
"""
|
||||
recieved_offers = read_json_api(args.port, 'offers', {'active': 'active', 'include_sent': False, 'coin_from': coin_from_data['id'], 'coin_to': coin_to_data['id']})
|
||||
print('recieved_offers', recieved_offers)
|
||||
|
||||
TODO - adjust rates based on extisting offers
|
||||
"""
|
||||
|
||||
rates = read_json_api('rates', {'coin_from': coin_from_data['id'], 'coin_to': coin_to_data['id']})
|
||||
print('Rates', rates)
|
||||
coingecko_rate = float(rates['coingecko']['rate_inferred'])
|
||||
use_rate = coingecko_rate
|
||||
@@ -137,21 +286,282 @@ def main():
|
||||
use_rate = offer_template['minrate']
|
||||
|
||||
print('Creating offer for: {} at rate: {}'.format(offer_template, use_rate))
|
||||
template_from_addr = offer_template['address']
|
||||
offer_data = {
|
||||
'addr_from': offer_template['address'],
|
||||
'addr_from': -1 if template_from_addr == 'auto' else template_from_addr,
|
||||
'coin_from': coin_from_data['ticker'],
|
||||
'coin_to': coin_to_data['ticker'],
|
||||
'amt_from': offer_template['amount'],
|
||||
'amt_var': offer_template['amount_variable'],
|
||||
'valid_for_seconds': offer_template.get('offer_valid_seconds', config.get('offer_valid_seconds', 3600)),
|
||||
'rate': use_rate,
|
||||
'swap_type': 'adaptor_sig',
|
||||
'lockhrs': '24',
|
||||
'automation_strat_id': 1}
|
||||
new_offer = read_json_api(args.port, 'offers/new', offer_data)
|
||||
print('New offer: {}'.format(new_offer))
|
||||
except Exception as e:
|
||||
print('Error: Clamping rate to minimum.')
|
||||
if args.debug:
|
||||
print('offer data {}'.format(offer_data))
|
||||
new_offer = read_json_api('offers/new', offer_data)
|
||||
print('New offer: {}'.format(new_offer['offer_id']))
|
||||
num_state_changes += 1
|
||||
if not 'offers' in script_state:
|
||||
script_state['offers'] = {}
|
||||
template_name = offer_template['name']
|
||||
if not template_name in script_state['offers']:
|
||||
script_state['offers'][template_name] = []
|
||||
script_state['offers'][template_name].append({'offer_id': new_offer['offer_id'], 'time': int(time.time())})
|
||||
max_seconds_between_offers = config['max_seconds_between_offers']
|
||||
min_seconds_between_offers = config['min_seconds_between_offers']
|
||||
if max_seconds_between_offers > min_seconds_between_offers:
|
||||
time_between_offers = random.randint(min_seconds_between_offers, max_seconds_between_offers)
|
||||
else:
|
||||
time_between_offers = min_seconds_between_offers
|
||||
script_state['delay_next_offer_before'] = int(time.time()) + time_between_offers
|
||||
|
||||
if args.debug and len(bid_templates) > 0:
|
||||
print('Processing {} bid templates'.format(config['num_enabled_bids']))
|
||||
for bid_template in bid_templates:
|
||||
|
||||
delay_next_bid_before = script_state.get('delay_next_bid_before', 0)
|
||||
if delay_next_bid_before > int(time.time()):
|
||||
print('Delaying bids until {}'.format(delay_next_bid_before))
|
||||
break
|
||||
|
||||
# Check bids in progress
|
||||
max_concurrent = bid_template.get('max_concurrent', 1)
|
||||
if not 'bids' in script_state:
|
||||
script_state['bids'] = {}
|
||||
template_name = bid_template['name']
|
||||
if not template_name in script_state['bids']:
|
||||
script_state['bids'][template_name] = []
|
||||
previous_bids = script_state['bids'][template_name]
|
||||
|
||||
bids_in_progress: int = 0
|
||||
for previous_bid in previous_bids:
|
||||
if not previous_bid['active']:
|
||||
continue
|
||||
previous_bid_id = previous_bid['bid_id']
|
||||
previous_bid_info = read_json_api(f'bids/{previous_bid_id}')
|
||||
bid_state = previous_bid_info['bid_state']
|
||||
if bid_state in ('Completed', 'Timed-out', 'Abandoned', 'Error', 'Rejected'):
|
||||
print(f'Marking bid inactive {previous_bid_id}, state {bid_state}')
|
||||
previous_bid['active'] = False
|
||||
num_state_changes += 1
|
||||
continue
|
||||
if bid_state in ('Sent', 'Received') and previous_bid_info['expired_at'] < int(time.time()):
|
||||
print(f'Marking bid inactive {previous_bid_id}, expired')
|
||||
previous_bid['active'] = False
|
||||
num_state_changes += 1
|
||||
continue
|
||||
bids_in_progress += 1
|
||||
|
||||
if bids_in_progress >= max_concurrent:
|
||||
print('Max concurrent bids reached for template')
|
||||
continue
|
||||
|
||||
# Bidder sends coin_to and receives coin_from
|
||||
coin_from_data = coins_map[bid_template['coin_from']]
|
||||
coin_to_data = coins_map[bid_template['coin_to']]
|
||||
|
||||
offers_options = {
|
||||
'active': 'active',
|
||||
'include_sent': False,
|
||||
'coin_from': coin_from_data['id'],
|
||||
'coin_to': coin_to_data['id'],
|
||||
'with_extra_info': True,
|
||||
'sort_by': 'rate',
|
||||
'sort_dir': 'asc',
|
||||
}
|
||||
|
||||
recieved_offers = read_json_api('offers', offers_options)
|
||||
print('recieved_offers', recieved_offers)
|
||||
|
||||
for offer in recieved_offers:
|
||||
offer_id = offer['offer_id']
|
||||
offer_amount = float(offer['amount_from'])
|
||||
offer_rate = float(offer['rate'])
|
||||
bid_amount = offer_amount
|
||||
|
||||
min_swap_amount = bid_template.get('min_swap_amount', 0.01) # TODO: Make default vary per coin
|
||||
can_adjust_amount: bool = offer['amount_negotiable'] and bid_template.get('amount_variable', True)
|
||||
if can_adjust_amount is False and offer_amount > bid_template['amount']:
|
||||
if args.debug:
|
||||
print(f'Bid amount too low for offer {offer_id}')
|
||||
continue
|
||||
if (can_adjust_amount is False and offer_amount < bid_template['amount']) or offer_amount < min_swap_amount:
|
||||
if args.debug:
|
||||
print(f'Offer amount too low for bid {offer_id}')
|
||||
continue
|
||||
|
||||
if offer_rate > bid_template['maxrate']:
|
||||
if args.debug:
|
||||
print(f'Bid rate too low for offer {offer_id}')
|
||||
continue
|
||||
|
||||
sent_bids = read_json_api('sentbids', {'offer_id': offer['offer_id'], 'with_available_or_active': True})
|
||||
if len(sent_bids) > 0:
|
||||
if args.debug:
|
||||
print(f'Already bidding on offer {offer_id}')
|
||||
continue
|
||||
|
||||
offer_identity = read_json_api('identities/{}'.format(offer['addr_from']))
|
||||
if len(offer_identity) > 0:
|
||||
successful_sent_bids = offer_identity[0]['num_sent_bids_successful']
|
||||
failed_sent_bids = offer_identity[0]['num_sent_bids_failed']
|
||||
if failed_sent_bids > 3 and failed_sent_bids > successful_sent_bids:
|
||||
if args.debug:
|
||||
print(f'Not bidding on offer {offer_id}, too many failed bids ({failed_sent_bids}).')
|
||||
continue
|
||||
|
||||
max_coin_from_balance = bid_template.get('max_coin_from_balance', -1)
|
||||
if max_coin_from_balance > 0:
|
||||
wallet_from = read_json_api_wallet('wallets/{}'.format(coin_from_data['ticker']))
|
||||
total_balance_from = float(wallet_from['balance']) + float(wallet_from['unconfirmed'])
|
||||
if args.debug:
|
||||
print(f'Total coin from balance {total_balance_from}')
|
||||
if total_balance_from + bid_amount > max_coin_from_balance:
|
||||
if can_adjust_amount and max_coin_from_balance - total_balance_from > min_swap_amount:
|
||||
bid_amount = max_coin_from_balance - total_balance_from
|
||||
print(f'Reduced bid amount to {bid_amount}')
|
||||
else:
|
||||
if args.debug:
|
||||
print(f'Bid amount would exceed maximum wallet total for offer {offer_id}')
|
||||
continue
|
||||
|
||||
min_coin_to_balance = bid_template['min_coin_to_balance']
|
||||
if min_coin_to_balance > 0:
|
||||
wallet_to = read_json_api_wallet('wallets/{}'.format(coin_to_data['ticker']))
|
||||
|
||||
total_balance_to = float(wallet_to['balance']) + float(wallet_to['unconfirmed'])
|
||||
if args.debug:
|
||||
print(f'Total coin to balance {total_balance_to}')
|
||||
|
||||
swap_amount_to = bid_amount * offer_rate
|
||||
if total_balance_to - swap_amount_to < min_coin_to_balance:
|
||||
if can_adjust_amount:
|
||||
adjusted_swap_amount_to = total_balance_to - min_coin_to_balance
|
||||
adjusted_bid_amount = adjusted_swap_amount_to / offer_rate
|
||||
|
||||
if adjusted_bid_amount > min_swap_amount:
|
||||
print(f'Reduced bid amount to {bid_amount}')
|
||||
bid_amount = adjusted_bid_amount
|
||||
swap_amount_to = adjusted_bid_amount * offer_rate
|
||||
|
||||
if total_balance_to - swap_amount_to < min_coin_to_balance:
|
||||
if args.debug:
|
||||
print(f'Bid amount would exceed minimum coin to wallet total for offer {offer_id}')
|
||||
continue
|
||||
|
||||
bid_data = {
|
||||
'offer_id': offer['offer_id'],
|
||||
'amount_from': bid_amount}
|
||||
|
||||
if 'address' in bid_template:
|
||||
addr_from = bid_template['address']
|
||||
if addr_from != -1 and addr_from != 'auto':
|
||||
bid_data['addr_from'] = addr_from
|
||||
|
||||
if config.get('test_mode', False):
|
||||
print('Would create bid: {}'.format(bid_data))
|
||||
bid_id = 'simulated'
|
||||
else:
|
||||
if args.debug:
|
||||
print('Creating bid: {}'.format(bid_data))
|
||||
new_bid = read_json_api('bids/new', bid_data)
|
||||
print('New bid: {} on offer {}'.format(new_bid['bid_id'], offer['offer_id']))
|
||||
bid_id = new_bid['bid_id']
|
||||
|
||||
num_state_changes += 1
|
||||
script_state['bids'][template_name].append({'bid_id': bid_id, 'time': int(time.time()), 'active': True})
|
||||
|
||||
max_seconds_between_bids = config['max_seconds_between_bids']
|
||||
min_seconds_between_bids = config['min_seconds_between_bids']
|
||||
if max_seconds_between_bids > min_seconds_between_bids:
|
||||
time_between_bids = random.randint(min_seconds_between_bids, max_seconds_between_bids)
|
||||
else:
|
||||
time_between_bids = min_seconds_between_bids
|
||||
script_state['delay_next_bid_before'] = int(time.time()) + time_between_bids
|
||||
break # Create max one bid per iteration
|
||||
|
||||
if args.debug and len(stealthex_swaps) > 0:
|
||||
print('Processing {} stealthex templates'.format(config['num_enabled_swaps']))
|
||||
for stealthex_swap in stealthex_swaps:
|
||||
if stealthex_swap.get('enabled', True) is False:
|
||||
continue
|
||||
coin_from_data = coins_map[stealthex_swap['coin_from']]
|
||||
|
||||
wallet_from = read_json_api_wallet('wallets/{}'.format(coin_from_data['ticker']))
|
||||
|
||||
current_balance = float(wallet_from['balance'])
|
||||
|
||||
min_balance_from = float(stealthex_swap['min_balance_from'])
|
||||
min_swap_amount = float(stealthex_swap['min_amount_tx'])
|
||||
max_swap_amount = float(stealthex_swap['max_amount_tx'])
|
||||
|
||||
# TODO: Check range limits
|
||||
|
||||
if current_balance >= min_balance_from + min_swap_amount:
|
||||
swap_amount = max_swap_amount
|
||||
if current_balance - swap_amount < min_balance_from:
|
||||
swap_amount = max(min_swap_amount, current_balance - min_balance_from)
|
||||
|
||||
estimate_url = 'https://api.stealthex.io/api/v2/estimate/{}/{}?amount={}&api_key={}&fixed=true'.format(coin_from_data['ticker'].lower(), stealthex_swap['coin_to'].lower(), swap_amount, stealthex_swap['api_key'])
|
||||
if args.debug:
|
||||
print(f'Estimate URL: {estimate_url}')
|
||||
estimate_response = json.loads(post_req(estimate_url))
|
||||
|
||||
amount_to = float(estimate_response['estimated_amount'])
|
||||
rate = swap_amount / amount_to
|
||||
min_rate = float(stealthex_swap['min_rate'])
|
||||
if rate < min_rate:
|
||||
if args.debug:
|
||||
print('Stealthex rate {} below minimum {} for {} to {}'.format(rate, min_rate, coin_from_data['ticker'], stealthex_swap['coin_to']))
|
||||
continue
|
||||
|
||||
exchange_url = 'https://api.stealthex.io/api/v2/exchange?api_key={}'.format(stealthex_swap['api_key'])
|
||||
|
||||
address_to = stealthex_swap.get('receive_address', 'auto')
|
||||
if address_to == 'auto':
|
||||
address_to = read_json_api('wallets/{}/nextdepositaddr'.format(stealthex_swap['coin_to']))
|
||||
|
||||
address_refund = stealthex_swap.get('refund_address', 'auto')
|
||||
if address_refund == 'auto':
|
||||
address_refund = read_json_api('wallets/{}/nextdepositaddr'.format(coin_from_data['ticker']))
|
||||
|
||||
exchange_data = {
|
||||
'currency_from': coin_from_data['ticker'].lower(),
|
||||
'currency_to': stealthex_swap['coin_to'].lower(),
|
||||
'address_to': address_to,
|
||||
'amount_from': swap_amount,
|
||||
'fixed': True,
|
||||
#'extra_id_to':
|
||||
#'referral':
|
||||
'refund_address': address_refund,
|
||||
#'refund_extra_id':
|
||||
'rate_id': estimate_response['rate_id'],
|
||||
}
|
||||
|
||||
if args.debug:
|
||||
print(f'Exchange URL: {estimate_url}')
|
||||
print(f'Exchange data: {exchange_data}')
|
||||
|
||||
exchange_response = json.loads(post_req(exchange_url, exchange_data))
|
||||
|
||||
if 'Error' in estimate_response:
|
||||
raise ValueError('Exchange error ' + estimate_response)
|
||||
|
||||
raise ValueError('TODO')
|
||||
|
||||
if num_state_changes > 0:
|
||||
if os.path.exists(args.statefile):
|
||||
shutil.copyfile(args.statefile, args.statefile + '.last')
|
||||
with open(args.statefile, 'w') as fp:
|
||||
json.dump(script_state, fp, indent=4)
|
||||
|
||||
except Exception as e:
|
||||
print(f'Error: {e}.')
|
||||
|
||||
if args.oneshot:
|
||||
break
|
||||
print('Looping indefinitely, ctrl+c to exit.')
|
||||
delay_event.wait(60)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user