User:Matt/Pricebot

From AAPG Wiki
Jump to navigation Jump to search

Every 24 hours, pricebot updates Exchange_rate subpages with the current exchanges rates. These are in turn used by Property:Cost to provide on-the-fly currency conversion. To see this in action, hover your mouse here: cost::25,000 GBP.

It can also get crude and natural gas prices, but the page Crude price/Benchmarks is not configured in this wiki.

Pricebot is a Python program running on the server that runs the wiki. It gets the price form the Yahoo! Finance API. Here's what it looks like:

# Pricebot
# Matt Hall, 2013
# Apache license v2.0
#
# This is a wiki bot for placing exchange rates and crude oil prices in a MediaWiki wiki
# You need to make pages Exchange_rate/Currencies and Crude_price/Benchmarks to pass the
# list of ticker symbols to the bot. If the pages don't exist, the bot does nothing.
# 
# See http://www.subsurfwiki.org/wiki/SubSurfWiki:Pricebot for more help and examples.
# 
# You may want to add this script to your crontab, to run every hour, say:
# 00 */1 * * * /path/to/scripts/pricebot.py
# 

# Import libraries
import mwclient
import urllib2
import time

from config import config

BOT_NAME = config.bot_name
PASSWORD = config.bot_pword

WIKI_URL = 'wiki.aapg.org'
WIKI_PATH = '/'

# Set globals
BASE_URL = "http://query.yahooapis.com/v1/public/yql"
FORMATS = "&format=xml&diagnostics=false&env=http%3A%2F%2Fdatatables.org%2Falltables.env"
RETRY_TIME = 3.0
STOP_WORDS = ['off', 'stop', 'shutdown']
STATUS_PAGE  = 'User:Pricebot/Status'

def bot_status(site):
    # Check the bot's status page
    for word in STOP_WORDS:
        if word in site.Pages[STATUS_PAGE].edit().lower():
            print "Bot stopped by Status page"
            return 0
    return 1
	
    # Could also block the bot's user with a button on the bot's page	

def set_exchange_rates(site):
    # Prepare the list of currencies to grab
    curr_page  = site.Pages['Exchange_rate/Currencies'].edit()
    currencies = [ str(i.strip()) for i in curr_page.split('*')[1:] ]

    # Step over the currencies and write the rates back to the subpages
    for currency in currencies:

        # get the XML from Yahoo Finance
        query = "?q=select%20Rate%20from%20yahoo.finance.xchange%20where%20pair=%22USD{0}%22".format(currency)
        url = BASE_URL + query + FORMATS

        errors = 0
        while errors < 4:
            try:
                text = urllib2.urlopen(url).read()
                break
            except urllib2.HTTPError:
                errors += 1
                time.sleep(RETRY_TIME)
                pass                

        if '<Rate>' in text:
            # Extract the prices from the XML
            rate = text.split('<Rate>')[1].split('</Rate>')[0]

            # Save the result back to the relevant subpage
            page = site.Pages[ 'Exchange_rate/'+currency ]
            update_time = ' — Last updated {0}'.format(time.ctime())
            page.save(rate+update_time,summary='Regular update from Yahoo Finance')
            print currency, "=", rate

        else:
            # Timed out
            print currency, "failed"
            continue

    return None

def set_crude_prices(site):
    # Prepare the list of benchmarks to grab
    bench_page  = site.Pages['Crude_price/Benchmarks'].edit()
    benchmarks = [ str(i.strip()) for i in bench_page.split('*')[1:] ]
	
    # If we want WCC or other differential, we need CL
    # And we need to remember CL to compute them
    if 'WCC' in benchmarks and 'CL' not in benchmarks:
        benchmarks.insert(0,'CL')
    CL = 0

    # We compute a time 45 days in the future for a price
    future = time.gmtime( time.time() + 45*24*60*60 ) 
    month = future.tm_mon
    month_codes = ['F','G','H','J','K','M','N','Q','U','V','X','Z']
    month = month_codes[month - 1]
    year = str(future.tm_year)[-2:]

    # Step over the benchmarks and write the prices back to the subpages
    for benchmark in benchmarks:
        
        # get the XML from Yahoo Finance
        symbol = benchmark + month + year + ".NYM"
        query = "?q=select%20LastTradePriceOnly%20from%20yahoo.finance.quotes%20where%20symbol=%22{0}%22".format(symbol)
        url = BASE_URL + query + FORMATS

        errors = 0
        while errors < 4:
            try:
                text = urllib2.urlopen(url).read()
                break
            except urllib2.HTTPError:
                errors += 1
                time.sleep(RETRY_TIME)
                pass                

        # Extract the prices from the XML
        if '<LastTradePriceOnly>' in text:
            price = text.split("<LastTradePriceOnly>")[1].split("</LastTradePriceOnly>")[0]
        
            # Capture the CL price
            if benchmark == "CL":
                CL = float(price)

            if benchmark == "WCC":
                if CL == 0:
                    # then CL failed and WCC must fail too
                    print "WCC cannot be computed because CL failed"
                    continue
                else:
                    price = str(CL + float(price))

            # Save the result back to the relevant subpage
            page = site.Pages['Crude_price/'+benchmark ]
            update_time = ' — Last updated {0}'.format(time.ctime())
            page.save(price+update_time,summary='Regular update from Yahoo Finance')
            print benchmark, "=", price
            
        else:
            # Timed out
            print benchmark, "failed"
            continue

    return None

# Now do the work!
# First, pass credentials
wiki = mwclient.Site(WIKI_URL, path=WIKI_PATH)
wiki.login(BOT_NAME, PASSWORD)

# Then check if the bot is disabled, and act accordingly
if bot_status(wiki) == 1:
    set_exchange_rates(wiki)
    set_crude_prices(wiki)