Skip to content

Instantly share code, notes, and snippets.

@codekoala
Last active November 26, 2015 07:41
Show Gist options
  • Save codekoala/cf8796a95c15cf721962 to your computer and use it in GitHub Desktop.
Save codekoala/cf8796a95c15cf721962 to your computer and use it in GitHub Desktop.
Pull current pricing information for one or more tickers using OTC Markets
// otc is a simple program that gets current pricing data from OTC Markets for
// one or more tickers
package main
import (
"fmt"
"io/ioutil"
"math"
"net/http"
"os"
"time"
"github.com/robertkrimen/otto"
)
const (
WORKERS = 50
QUEUE_MULTIPLIER = 3
)
var remaining int
func init() {
remaining = len(os.Args) - 1
if remaining == 0 {
fmt.Printf("Usage: %s TICKER [TICKER ...]\n", os.Args[0])
os.Exit(-1)
}
}
func main() {
rem_f := float64(remaining)
// make a channel to keep track of all of the tickers for which data needs
// to be requested
queueSize := int(math.Min(rem_f, WORKERS*QUEUE_MULTIPLIER))
tickers := make(chan string, queueSize)
// make a channel that each goroutine will use to pass back the "JSON"
// returned by OTC Markets
resSize := int(math.Min(rem_f, WORKERS))
otcJson := make(chan []byte, resSize)
// start a handful of workers in goroutines so we don't exhaust our open
// file limits when fetching a large number of tickers
for i := 0; i < resSize; i++ {
go fetchData(tickers, otcJson)
}
// queue up each ticker specified on the command line in a goroutine so we
// can still process results should our channel get full
go func() {
for _, ticker := range os.Args[1:] {
tickers <- ticker
}
}()
// create a new Otto runtime
Otto := otto.New()
var (
output []byte
value otto.Value
)
// continue waiting for data until we've processed all tickers
for len(tickers) > 0 || len(otcJson) > 0 || remaining > 0 {
select {
case output = <-otcJson:
// evaluate the OTC "JSON" and convert it into proper JSON
Otto.Run(fmt.Sprintf(`myvar = JSON.stringify(eval('%s'));`, output))
// pull the JSON value from the runtime
value, _ = Otto.Get("myvar")
// display the real JSON
fmt.Println(value)
case <-time.After(50 * time.Millisecond):
// give some time for the goroutines to fetch the data
break
}
}
}
// fetchData retrieves pricing data for one or more tickers and sends it back
// across a channel. This function is meant to be invoked as a goroutine.
func fetchData(tickers chan string, out chan []byte) {
var (
ticker string
res *http.Response
err error
body []byte
)
for {
select {
case ticker = <-tickers:
// request the data for our ticker
res, err = http.Get("http://www.otcmarkets.com/otciq/ajax/getTradeAndInside.json?symbol=" + ticker)
remaining--
if err != nil {
fmt.Printf("Failed to get data for %s: %s\n", ticker, err)
continue
}
// read everything from the response
body, err = ioutil.ReadAll(res.Body)
res.Body.Close()
if err != nil {
fmt.Printf("Failed to read data for %s: %s\n", ticker, err)
continue
}
// pass the ticker info back using a channel for further processing
out <- body
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment