Last active
November 26, 2015 07:41
-
-
Save codekoala/cf8796a95c15cf721962 to your computer and use it in GitHub Desktop.
Pull current pricing information for one or more tickers using OTC Markets
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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