StockPlay — Stock-like trading game

This module provides a simulated stock trading game. Requires and api key from https://www.alphavantage.co/ to fetch stock quotes.

Most commands require that the player login as described in the NickUser module.

Note that it is important to configure api limitations when configuring this module. The alphavantage.co api allows a maximum of 5 requests per minute and 500 requests per day. For reasonable trading - that is, executing trades at the current market price - we need to be able to lookup the price of any symbol at any time. Likewise, to generate reports we need to keep the prices of all symbols somewhat up to date. This happens at some interval - see bginterval.

Considering the daily limit means, when evenly spread, we can sent a request no more often than 173 seconds: (24 * 60 * 60 / 500) - and therefore, the value of bginterval must be some value larger than 173, as this value will completely consume the daily limit.

When trading, the price of the traded symbol is allowed to be tcachesecs seconds old before the API will be used to fetch a more recent price. This value must be balanced against bginterval depending on your trade frequency and variety.

Background or batch-style tasks that rely on symbol prices run afoul with the above constraints - but in a magnified way as they rely on api-provided data to calculate player stats across many players at a time. The rcachesecs setting controls the maximum price age before the API is hit.

Commands

.buy <amount> <symbol>

Buy some number of the specified symbol such as “.buy 10 amd”

.sell <amount> <symbol>

Sell similar to .buy

.port [<player>] [<full>]

Get a report on the calling player’s portfolio. Another player’s name can be passed as an argument to retrieve information about a player other than the calling player. Finally, the ‘full’ argument can be added to retrieve a full listing of the player’s holdings. Per the above, values based on symbol prices may be delayed based on the rcachesecs config setting.

Config

{
    "startbalance": 10000,
    "tradedelay": 0,
    "apikey": "xxxxxxxxxxxxxx",
    "tcachesecs": 300,
    "rcachesecs": 14400,
    "bginterval": 300,
    "midnight_offset": 0
}
startbalance

Number of dollars that players start with

tradedelay

Delay in seconds between differing trades of the same symbol. Multiple buys OR multiple sells are allowed, but not a mix.

NOT IMPLEMENTED

apikey

API key from https://www.alphavantage.co/support/#api-key

tcachesecs

When performing a trade, how old of a cached symbol price is permitted before fetching from API.

Recommended ~30 minutes (1800)

rcachesecs

When calculating a portfolio report, how old of a cached symbol price is permitted before fetching from API.

Recommended ~4 hours (14400)

bginterval

Symbol prices are updated in the background. This is necessary because fetching a portfolio report may require fetching many symbol prices. The alphavantage.co api allows only 5 calls per minute. Because of this limitation, fetching a report would take multiple minutes with more than 5 symbols, which would not work.

For this reason, we update symbols at a low interval in the background. Every bginterval seconds, a task will be started that updates the price of symbols older than rcachesecs.

Estimated 5 minute (300), but likely will need tuning depending on playerbase

midnight_offset

Number of seconds added to the clock when calculating midnight.

At midnight, the bot logs all player balances for use in gain/loss over time calculations later on. If you want this to happen at midnight system time, leave this at 0. Otherwise, it can be set to some number of seconds to e.g. to compensate for time zones.

Default: 0

Class Reference

class pyircbot.modules.StockPlay.StockPlay(bot, moduleName)[source]

Bases: pyircbot.modulebase.ModuleBase

build_report(nick)[source]

Return a dict containing the player’s cash, stock value, holdings listing, and 24 hour statistics.

calc_user_avgbuy(nick, symbol)[source]

Calculate the average buy price of a user’s stock. This is generated by backtracking through their buy/sell history :return: price, in cents

check_nick(nick)[source]

Set up a user’s account by setting the initial balance

checksym(s)[source]

Validate that a string looks like a stock symbol

cmd_buy(*args, **kwargs)
cmd_port(*args, **kwargs)
cmd_sell(*args, **kwargs)
cmd_top(message, command)[source]

Top 10 report command

do_report(lookup, sender, replyto, full)[source]

Generate a text report of the nick’s portfolio

<player> .port profit full
<bloomberg_terminal> player: profit has cash: $491.02 stock value: ~$11,137.32 total: ~$11,628.34 (24h +1,504.37 (14.86%)⬆)
<bloomberg_terminal> player:   1 AAPL bought at average $170.41  +3.92   (2.30%)⬆ now $174.33
<bloomberg_terminal> player:  14 AMD  bought at average  $23.05  +1.16   (5.03%)⬆ now  $24.21
<bloomberg_terminal> player:  25 DBX  bought at average  $25.42  -1.08  (-4.25%)⬇ now  $24.34
<bloomberg_terminal> player:  10 DENN bought at average  $17.94  -0.27  (-1.51%)⬇ now  $17.67
<bloomberg_terminal> player:  18 EA   bought at average  $99.77  -1.27  (-1.28%)⬇ now  $98.50
<bloomberg_terminal> player:  10 INTC bought at average  $53.23  +0.00   (0.00%)⬆ now  $53.23
<bloomberg_terminal> player: 160 KPTI bought at average   $4.88  +0.00   (0.00%)⬆ now   $4.88
do_tasks()[source]

Do interval tasks such as recording nightly balances

do_topten(nick, replyto)[source]

Do lookup of highest valued portfolios

do_trade(trade)[source]

Perform a queued trade

Parameters:trade (Trade) – trade struct to perform
fetch_priceinfo(symbol)[source]

Request a stock quote from the API. The API provides the format:

{'Global Quote': {
 {'01. symbol': 'MSFT',
  '02. open': '104.3900',
  '03. high': '105.7800',
  '04. low': '104.2603',
  '05. price': '105.6700',
  '06. volume': '21461093',
  '07. latest trading day':'2019-02-08',
  '08. previous close':'105.2700',
  '09. change': '0.4000',
  '10. change percent': '0.3800%'}}

Reformat as:

{'symbol': 'AMD',
 'open': '22.3300',
 'high': '23.2750',
 'low': '22.2700',
 'price': '23.0500',
 'volume': '78129280',
 'latest trading day': '2019-02-08',
 'previous close': '22.6700',
 'change': '0.3800',
 'change percent': '1.6762%'}
get_bal(nick)[source]

Get player’s balance :return: balance in cents

get_holding(nick, symbol)[source]

Return the number of stocks of a certain symbol a player has

get_latest_hist_bal(nick)[source]

Return the most recent historical balance of a player. Aka their “opening” value.

get_price(symbol, thresh=None)[source]

Get symbol price, with quote being at most $thresh seconds old

get_priceinfo_cached(symbol, thresh)[source]

Return the cached symbol price if it’s more recent than the last 15 minutes Otherwise, fetch the price then cache and return it.

log_trade(nick, time, type, symbol, count, price)[source]

Append a record of a trade to the database log

nick_exists(name)[source]

Check whether a nick has a record

ondisable()[source]
price_updater()[source]

Perform quote cache updating task

record_nightly_balances()[source]

Create a record for each user’s balance at the start of each day.

set_bal(nick, amount)[source]

Set a player’s balance

Parameters:amount – new balance in cents
set_holding(nick, symbol, count)[source]

Set the number of stocks of a certain symbol a player that

trader_background()[source]

Perform trading, reporting and other background tasks

class pyircbot.modules.StockPlay.Trade(nick, buy, symbol, amount, replyto)

Bases: tuple

amount

Alias for field number 3

buy

Alias for field number 1

nick

Alias for field number 0

replyto

Alias for field number 4

symbol

Alias for field number 2

pyircbot.modules.StockPlay.calc_gain(start, end)[source]

Calculate the +/- gain percent given start/end values :return: Decimal

pyircbot.modules.StockPlay.format_decimal(decm, prefix='$', plus=False)[source]

Formats a decimal as a dollar value

pyircbot.modules.StockPlay.format_gainloss(diff, pct)[source]

Formats a difference and percent change as “+0.00 (0.52%)⬆” with appropriate IRC colors

pyircbot.modules.StockPlay.format_gainloss_inner(diff, pct)[source]

Formats a difference and percent change as “+0.00 (0.52%)⬆” with appropriate IRC colors

pyircbot.modules.StockPlay.format_price(cents, prefix='$', plus=False)[source]

Formats cents as a dollar value

pyircbot.modules.StockPlay.tabulate(rows, justify=None)[source]
Parameters:
  • rows – list of lists making up the table data
  • justify – array of True/False to enable left justification of text