Tour de Finance (TDF)

The Tour de Finance (TDF) is an open-source paper-trading system designed to facilitate the research of and education in the application of controls in finance. Using TDF, several users can join competitions in which they write autonomous scripts which retrieve historical stock prices on the S&P 500 and then make intelligent trades based on that information.

Table of Contents

Admins

Installing from Source

If you wish to install TDF from source (recommended for large-scale deploys and contributors), you can pull the code from github.com/idealabs/tdf.

The README in the root of this repo will give more detailed instructions on how to install the dependencies and run the TDF server.

Installing and Running a VirtualBox Image of TDF

A recent build of TDF can be downloaded, imported into, and run from Oracle VM VirtualBox without requiring the installation of the TDF source or its dependencies. To do this:

  1. Download and Install VirtualBox from virtualbox.org/wiki/Downloads. This site will have installation instructions particular to your host operating system's needs.
  2. Download the TDF Virtual Appliance by clicking the link idealabs.byu.edu/vms/tour-de-finance.ova. The application will automatically download into your browser's default download directory.
  3. Start the VirtualBox application. You should get a dashboard similar to that shown below.
  4. From the toolbar, navigate to and click on File > Import Appliance... (see figure below). This will open a dialogue box titled Import Virtual Appliance.
  5. Within the Import Virtual Appliance dialogue box (see figure below), click on the button Open appliance... This will open your operating system's file browser.
  6. Within the file browser opened in the previous step, navigate to the location where you downloaded the TDF Virtual Appliance in Step 2. Double click on the file tour-de-finance.ova or select it and click Open. You will return to the Import Virtual Appliance dialogue box.
  7. Within the Import Virtual Appliance dialogue box, verify that the path to the tour-de-finance.ova, shown to the right of the Open appliance... button (see figure below), is correct. If so, click on the Next button at the bottom of the dialogue box.
  8. The Import Virtual Appliance dialogue box now contains a series of options defining the import (see figure below). Leave these options at their defaults and click the Import button at the bottom of the dialogue box. A progress bar (see figure below) will appear showing the progress of the import.
  9. Once the import is completed, you will return to the VirtualBox dashboard. As shown in the figure below, you should now see a new entry in the left column titled deploy_... (this name may change in future releases). Highlight this entry and click on the green Start button near the top of the dashboard. The TDF instance will begin powering up.
  10. While the TDF instance is powering up, a series of dialogue boxes may appear giving information about and instructions on the instance (such as those shown in the figures below). Click on the Next button or the Ok button on any of these dialogue boxes that appear.
  11. Your machine will boot up, showing details in the console such as those shown in the figure below.
  12. Eventually, your machine will be booted and the console will display a prompt to login as shown in the figure below. TDF is running in the background of this machine, so you can now minimize the virtual machine since you do not need to login or perform any additional action here.
  13. Now you can access TDF Web Application (see the figure below) on the machine running the virtual machine through the url http://localhost:3000. If you want to access TDF from a remote machine, you will need to forward port 3000 of this machine to a public location.
  14. If you ever need to close the virtual machine, just try to close it like any other program. (Red box in upper right hand corner). Then select Power off the Machine when the subsequent dialogue box (see the figure below) opens. TDF will then be exited safely.

Getting Started: Creating an Admin Account

As an admin, the first thing you will need to do is to create an admin account. To do this, use a browser to navigate to the TDF Webapp home page at localhost:3000. On this page, you should see a section titled New to Tour de Finance? Create an Account. An admin account is a regular account with the username admin; therefore, to create an admin account, fill out all the fields in this section and choose the username to be admin (see figure below).

After you have created an admin account, you will be automatically logged in as the admin and then redirected to the Existing Leagues page, as shown below (note that any time you try to access the home page after logging in with any account, you will be redirected to this page).

Creating a League

A league is a container that holds a set of players which trade and compete on a set of rules. Players will not be able to compete unless they join a league.

To create a new league, log in to the admin account (if you have not done so already) and navigate to the Existing Leagues page by either navigating to the TDF home or by clicking on the Leagues link on the top navigation bar (see figure at the end of the previous section).

This page shows a list of all the leagues created within TDF. Since this is a new TDF instance, this list will be empy. Click on the Add a New League button to begin the process of creating a new league.

You will now navigate to a page allowing you to set the various rules under which agents in the league will compete (see figures below). In summary, the rules are as follows:

  • General Settings
    • League Name: The name of the league
  • Competition Phases
    • Trial Period Start: The trial period is the period in which users can create new agents, test their algorithms, and reset as many times as they want. In essence, it is a competition with more lax rules and no winner is chosen at the end
    • Competition Period Start: The competition period locks the league so that no agents can be created or reset. Agents are reset immediately when the competition begins.
    • Competition Period End: At the end of the competition, agents are no longer able to trade and a winner is chosen (currently based only on who has the most money, but more metrics will be implemented in future releases).
  • Trade Settings
    • Starting Cash: The amount of cash in each agent's account upon creation and immediately following each reset.
    • Leverage Multiple: Defines how much each agent is able to leverage and short sell.

      If leverage and short selling is to be disallowed, set the leverage multiple to 0.

      Otherwise, let the leverage multiple be m, the total portfolio value after a proposed trade be v and the sum of the negative value of the portfolio (i.e. the sum of the cash, if cash is less than zero, with the value of each security owed due to short-selling) be n. Then a trade will be disallowed if v < m * n. Further, if this condition fails for any given agent when stock prices update, then random securities in the agent's portfolio will be sold until the condition passes.
  • Redistribution Dynamics: If enabled, agents' cash will be redistributed between portfolios depending on performance simulating investors in funds leaving poor-performing funds and joining higher-performing funds. This leads to a more fair competition incentivizing intelligent trading over lucky trading (see the paper Competion Dynamics in a Virtual Fund Management System: Tour de Finance for more details).

Once you have set your rules to your satisfaction, click on the Save button at the bottom of the page. You will now return to the Existing Leagues where you should see a new entry for your new league (see figure below).

Users

Registering

To create a new user with TDF, visit the TDF home page. Fill out the information requested in the New to Tour de Finance? Create an Account and click the Sign Up button (see figure below).

If registration is successful, you will automatically be logged in and redirected to the Existing Leagues page (see figure below).

Creating an Agent

To create an agent, you need to choose the league in which it will compete. On the Existing Leagues page, click the View League button next to the league in which you wish to compete. You will be taken to the League Standings page for this league (see the figure below).

The first section on this page, titled Current Standings will show a graph of the portfolio values of the agents over time during the trial and competition periods of the league. Since there are no agents in the league shown above, this graph is empty.

The second section on this page, titled Agents in League "<League Name>", lists all agents registered for this league as well as a brief summary of the agent's standing in the league. Since there are no agents registered for the league shown in the image above, this list is empty. To create a new agent, click on the Create an Agent button to the right of the section header. You will be taken to a page allowing you to enter the details about your new agent (see image below).

Give your new agent a name, and if you want, give a brief description. When finished, click on the Submit button. You will then be redirected to the Agent View page for the agent (see figure below).

On this page, note the agent's Public ID and API Key. These values are necessary to connect a back-end script to trade on this agent.

Making a Trade through the Webapp

TDF allows you to make manual trades on an agent through the webapp. On the Agent View page, click on the Trade button near the top of the page (you can also access this button next to the agent's entry on the League View page). This will navigate you to the page shown below.

Within the Trade section of this page, input any of the ticker symbols for a security in the S&500, and choose the quantity of that security to buy (positive quantity) or sell (negative quantity). If you want to trade on more than one security, click on the Add a Security button and a new line will appear allowing you to make another trade.

For example, in the image above, a trade has been set up to buy 20 shares of Google and sell 15 shares of Apple.

Once you have set up your trade, click on the Execute Trade button. TDF will then adjust your cash and security accounts according to the current value of each of the securities chosen.

If the league is currently in its trial period, you can also click on the Reset Trades button to revert your portfolio to owning no securities and to having the default amount of cash in your account.

Connecting a Script to TDF

You can also make trades using any script running off a computer that has access to the TDF server (if the TDF server is running on a public url, then any script with access to the internet can access it).

Let http://<host>/ be the URL to access the TDF home page. Further, let <agent-id> and <api-key> be the Public ID and API Key respectively found on the Agent View page. Finally, let the n securities which you wish to trade have ticker symbols <symbol-1>, <symbol-2>, ..., <symbol-n>, and the quantities of each to buy/sell be <quantity-1>, <quantity-2>, ..., <quantity-n> where <quantity-i> is positive if you wish to buy <security-i> and negative if you wish to sell <security-i>.

Then, any script can make a trade by opening the url

http://<host>/agents/trade/<agent-id>?apikey=<api-key>&<security-1>=<quantity-1>&<security-2>=<quantity-2>&<...>&<security-n>=<quantity-n>
			

For example, the following MATLAB script buys 20 shares of Google and sell 15 shares of Apple:

SITE = 'http://localhost:3000/agents/trade/;

% The agent's public id and private api key can both
% be found on the agent's view on the TDF site
AGENT_ID = '52a5ddc7bcdd762351000fad';
API_KEY = 'uyftqmgojiiygcljlqqmvbfrgqmqdhyr';

URL = strcat(SITE, AGENT_ID, '?apikey=', API_KEY);
buy = strcat(URL, '&GOOG=20', '&AAPL=-15');
urlread(buy);
			

Using the TDF Queries

Again, let http://<host>/ be the URL to access the TDF home page. The following queries may be helpful for scripts to gain the information they need to make intelligent trades:

List of Tradable Ticker Symbols

You can see all of the ticker symbols tracked by TDF through http://<host>/history/.

The result will be a JSON array in the following format:

[
	"<security-1>",
	"<security-2>",
	...
]
			

You can also access this information on the front-end web app by accessing http:/<host>/#!/histories/.

Status and History of a Security

You can see the details, including current and historical prices, of a particular security represented by ticker symbol <symbol> through http://<host>/history/<symbol>.

The result will be a JSON object in the format

{
    "current": {
        "ask": <real-time ask price>,
        "bid": <real-time bid price>,
        "last": <real-time last price>
    },
    "ask": {
        <JavaScript date of most recently scraped data>: <ask at date>,
        ...
        <JavaScript date of first scraped data>: <ask at date>
    },
    "bid": {
        <JavaScript date of most recently scraped data>: <bid at date>,
        ...
        <JavaScript date of first scraped data>: <bid at date>
    },
    "last": {
        <JavaScript date of most recently scraped data>: <last at date>,
        ...
        <JavaScript date of first scraped data>: <last at date>
    }
}
			

You can also et an overview and a graph of this information from the front-end web app through http:/<host>/#!/histories/<symbol>.

Current Status of All Securities

You can use the following query to return the current status (ask, bid, and/or last prices) of all securities in the S&P 500:

[host]/currentstatus?select=[select]
			

Select can be one of "all", "ask", "bid", and "last" (without the quotes). If no select parameter is passed, or if it is not one of these options, the query will default to "all."

If [select] is "all", the result will be a JSON object in the following format:

{
	"[symbol]": {
		ask: [current ask price of [symbol]],
		bid: [current bid price of [symbol]],
		last: [current last price of [symbol]]
	},
	...
}
			

If [select] is "ask," then the result will be a JSON object in the following format:

{
	"[symbol]": [current ask price of [symbol]],
	...
}
			
(and similarly for "bid" and "last").

History of All Securities

You can use the following query to return the historical prices of all securities:

[host]/allhistories?select=[select]&n=[N]
			

Select can be one of "all", "ask", "bid", and "last" (without the quotes). If no select parameter is passed, or if it is not one of these options, the query will default to "all."

n can be any positive integer. If no n query parameter is passed, the query will default to n=12. This defines the number of times which the server has recorded that the query will return (e.g. if the server fetches data every hour, n=12 returns the last 12 hours of data).

If select is "all", then the result will be a JSON object in the following format:

{
	[JavaScript date of first most recently scraped data]: {
		"[symbol]": {
			ask: [ask price of [symbol]],
			bid: [bid price of [symbol]],
			last: [last price of [symbol]]
		},
		...
	},
	...,
	[JavaScript date of nth most recently scraped data]: {
		...
	}
}
			

If select is "ask", then the result will be a JSON object in the following format:

{
	[JavaScript date of the first most recently scraped data]: {
		"[symbol]": [ask price of [symbol]]
	},
	...,
	[JavaScript date of the nth most recently scraped data]: {
		...
	}
}
			
(and similarly for "bid" and "last").

Status of an Owned Agent

Using the same information used to make a back-end trade, you can see an agent's current portfolio composition and value through http://<host>/agents/<agent-id>/composition?apikey=<api-key>.

The result will be a JSON object in the format

{
    "uninvested_cash": <Total Uninvested Cash>,
    "<Symbol 1>": {
        "quantity": <Number of shares invested in <Symbol 1>>,
        "price": <Price of one share of <Symbol 1>>,
        "value": <Value of investment in <Symbol 1>, quantity * price>
    },
    ...
    "<Symbol n>": {
        "quantity": <Number of shares invested in <Symbol n>>,
        "price": <Price of one share of <Symbol n>>,
        "value": <Value of investment in <Symbol n>, quantity * price>
    },
    "total_value": <Total value of portfolio = uninvested_cash + sum_{i = 1 to n} value(<symbol i>)>
}