Part 25: Create A Bidbot With Steem-Python

in #utopian-io6 years ago (edited)

steem-python.png

This tutorial is part of a series where different aspects of programming with steem-python are explained. Links to the other tutorials can be found in the curriculum section below. This part will discuss how a bidbot can be made.


Repository

https://github.com/steemit/steem-python

What will I learn

  • How does a bidbot work?
  • How to process incoming bids
  • Verify incorrect bids
  • Refund incorrect bids
  • Monitor voting power
  • Perform votes

Requirements

  • Python3.6
  • steem-python

Difficulty

  • intermediate

Tutorial

Setup

Download the files from Github. There 2 are files bidbot.py which contains the main code and classes.py which contains the classes Bid and Queue. bidbot.py takes two arguments which are the starting_block to start scanning the blockchain from and account on which the bidbot will be run.

Run scripts as following:
> python bidbot.py 23822234 steempytutorials

How does a bidbot work?

The idea of a bidbot is that users can bid on a the voting power of the bidbot account by sending SBD and a memo which contains the url of the post to be upvoted. To maximise the voting power a voting round ends when voting power reaches 100% and will go down to 98% when another round will start. This 2% drop in voting power equals a 100% vote. This bidbot will not take into account the profitability of a bid and such this responsibility lies with the users doing the bidding.

How to process incoming bids

Tutorial 23 goes into depth how to retrieve transfers from blocks. This tutorials continues on that code. Two classes will be used in this bot Bid and Queue. Every Bid consists of the user sending the bid, the amount(which is converted to an Amount object), the memo and a placeholder for a Post object.

class Bid():
    def __init__(self, user, amount, memo):
        self.user = user
        self.amount = Amount(amount)
        self.memo = memo
        self.post = 0

The Queue object has a dictionary list that will house all the bids as a (indentifier, bid_amount) pair, a variable total_sbd to keep track of the total value of all the bids. The Steem object to interact with Steem and the bot's account.

class Queue():
    def __init__(self, steem, account):
        self.list = {}
        self.total_sbd = 0
        self.steem = steem
        self.account = account

Each transaction is checked to be of the transfer type, if so the operation is processed.

for transaction in block['transactions']:
    self.process_transaction(transaction)


def process_transaction(self, transaction):
    if transaction['operations'][0][0] == 'transfer':
        operation = transaction['operations'][0][1]
        self.process_transfer(operation)

Only transfers going to the bidbot account are of importance. For each transfer which to equals the bidbot account a Bid object is created and passed on to the queue.

def process_transfer(self, operation):
    user = operation['from']
    amount = operation['amount']
    memo = operation['memo']
    to = operation['to']

    if to == self.account:
        bid = classes.Bid(user, amount, memo)
        self.queue.add_bid(bid)

The bid is verified to be correct, if not it will be returned to the user. When a bid has passed quality control it is added to the list dictionary. The identifier is used as the unique key while the bidding amount is used as the value. The total_sbd is updated, and will be used to determine the percentage of each bid when the round ends.

def add_bid(self, bid):
    print(f'\nProcessing bid from {bid.user} for {bid.amount}')

    if self.verify_bid(bid) == 1:
        print('Added bid to list\n')
        self.list[bid.post['identifier']] = bid.amount['amount']
        self.total_sbd += bid.amount['amount']

Verify incorrect bids

There are several ways in which a user can make an incorrect bid. The bot will reject the following 3 bids: a post which is already bid on, an url which is not correct and the incorrect asset used to bid, only SBD is accepted.

def verify_bid(self, bid):
    valid = 1

    while valid == 1:
        try:
            bid.post = Post(bid.memo)
            if bid.post['identifier'] in self.list:
                self.refund(bid, 'Already in list')
                valid = 0
        except Exception as e:
            self.refund(bid, 'Invalid url')
            valid = 0
            print(repr(e))

        if bid.amount['asset'] != 'SBD':
            self.refund(bid, 'Invalid asset')
            valid = 0
        break

    return valid

Verifying the url is done by creating a Post object. If this fails the url is not valid. When this passes the post['identifier'] is taken and checked against the list of bids. The asset used in the bid is verified by using the Amount class: bid.amount['asset']. When an incorrect bid is detected a refund is issued.

Refund incorrect bids

When an invalid bid is detected the bid is returned to the user with a memo containing the reason for the refund.

def refund(self, bid, memo):
    print('Refund: ', memo)
    self.steem.transfer(to=bid.user,
                        amount=bid.amount['amount'],
                        asset=bid.amount['asset'],
                        memo=memo,
                        account=self.account)

Monitor voting power

To evaluate the current voting power of the account the altered function voting_power() is used, the function is located in account.py from steem-Python

def voting_power(self):
    diff_in_seconds = (datetime.datetime.utcnow() - parse_time(
    self["last_vote_time"])).seconds
    regenerated_vp = diff_in_seconds * 10000 / 86400 / 5
    total_vp = (self["voting_power"] + regenerated_vp) / 100
    if total_vp>100:
        return 100
    else:
        return "%.2f" % total_vp

source


To optimise calls to this function the block time is used to determine when a minute has passed. The block time is found under block['timestamp'] and can be processed with datetime.strptime(). Each minute the voting_power is checked to be 100, if so a voting round will be started.

def process_timestamp(self, block):
    timestamp = block['timestamp']
    datetime_object = datetime.strptime(timestamp, '%Y-%m-%dT%H:%M:%S')
    if datetime_object.second == 0:
        account = Account(self.account)
        print(f'\nVoting power: {account.voting_power()}%\n')
        if account.voting_power() == 100:
            self.queue.run_voting_round()

Perform votes

List is a dictionary containing sets of (identifier, bid amount). Together with total_sbd the percentage share of each bid can be calculated as follows: bid_amount/total_sbd*100

def run_voting_round(self):
    print('Starting upvoting round')

    for post in self.list:
        upvote_pct = self.list[post]/self.total_sbd*100
        self.steem.vote(post, upvote_pct, account=self.account)
        print(f'Upvoted {post} {upvote_pct:.2f}%')
    self.reset()

    print('Finished upvoting round')

Votes are casted with steem.vote() which requires the identifier, upvote_percentage and upvote_account.

Running the script

Running the script will scan any blocks for incoming transfers to the specified account. In order to test the bot 5 transfers were made to @steempyturials. After which the the blockchain was scanned starting from the block of the first transfer. In these 5 transfers some bids were made incorrect on purpose.

Screenshot 2018-07-02 05.52.45.png

Running the script then led to the following output:

python bidbot.py 23822234 steempytutorials

Connected to: https://rpc.steemviz.com
Block: 23822234

Processing bid from juliank for 0.010 SBD
Added bid to list

Block: 23822235

Processing bid from juliank for 0.010 SBD
Refund:  Already in list

Block: 23822236
Block: 23822237

Processing bid from juliank for 0.030 SBD
Added bid to list

Block: 23822238

Processing bid from juliank for 0.010 STEEM
Refund:  Invalid asset

Block: 23822239

Processing bid from juliank for 0.010 SBD
Refund:  Invalid url

Block: 23822240
.
.
.
Block: 23822253

Voting power: 100%

Starting upvoting round:

Upvoted juliank/thesmallcityofopatij-201806302000001487 25.00%
Upvoted juliank/outoftitlesforallthewaterfalls-20180629213452777 75.00%

Finished upvoting round

Curriculum

Set up:
Filtering
Voting
Posting
Constructing
Rewards
Transfers
Account Analysis

The code for this tutorial can be found on GitHub!

This tutorial was written by @juliank.

Sort:  

So awesome to see this! Thank you

Thank you for your contribution.

  • Interesting topics for bidbot, good job.

Your contribution has been evaluated according to Utopian policies and guidelines, as well as a predefined set of questions pertaining to the category.

To view those questions and the relevant answers related to your post, click here.


Need help? Write a ticket on https://support.utopian.io/.
Chat with us on Discord.
[utopian-moderator]

Hey @steempytutorials
Thanks for contributing on Utopian.
We’re already looking forward to your next contribution!

Want to chat? Join us on Discord https://discord.gg/h52nFrV.

Vote for Utopian Witness!

It's very interesting

im new to python but want to make a bid bot but really confused. how can i run the scripts exactly. can i just have the full code and change some things

Coin Marketplace

STEEM 0.19
TRX 0.14
JST 0.030
BTC 60061.12
ETH 3196.84
USDT 1.00
SBD 2.45