// Copyright 2013-2014 Fuzamei tech Ltd. All rights reserved.
package tick
// 本文件实现oanda数据源的tick数据获取下载和保存
import (
"errors"
"fmt"
"io/ioutil"
"log"
"net/http"
"regexp"
"strconv"
"strings"
"time"
"dev.33.cn/markinfo"
"dev.33.cn/server/market"
)
type oandaPairStatistics struct {
Date time.Time
BuyPercent float64
Rates float64
Symbol string
}
var oanda_base = int(200)
var oanda_prev = make(map[string]*oandaPairStatistics)
//var fetchoandaurl = "http://fxtrade.oanda.com/lang/cns/analysis/open-position-ratios"
var fetchoandaurl = "https://www.oanda.com/forex-trading/analysis/open-position-ratios"
func getOandaStatistics(ch chan<- *oandaPairStatistics) (err error) {
html, err := httpDownload(fetchoandaurl)
if err != nil {
return err
}
regex := regexp.MustCompile("(?is)
]*>(.*?)<\\/ol>")
matches := regex.FindAllStringSubmatch(html, -1)
if len(matches) != 2 {
log.Println(html)
return errors.New("regex matche error.")
}
//货币对--BuyPercent--SellPercent
ratio_graph := get_span(matches[0][0])
//货币对--Rates
rates := get_span(matches[1][0])
//精确到微妙
t := match_time(html)
if len(ratio_graph)%3 == 0 && len(rates)%2 == 0 && len(ratio_graph)/3 == len(rates)/2 {
for i := 0; i < len(ratio_graph)/3; i++ {
ops := &oandaPairStatistics{}
ops.Symbol = strings.Replace(ratio_graph[3*i], "/", "", -1)
ops.BuyPercent, _ = strconv.ParseFloat(ratio_graph[3*i+1], 64)
ops.Rates, _ = strconv.ParseFloat(rates[2*i+1], 64)
ops.Date = time.Unix(t/1000, (t%1000)*(1e6))
sendOanda(ch, ops)
}
} else {
return errors.New("invalid data.")
}
return
}
func sendOanda(ch chan<- *oandaPairStatistics, data *oandaPairStatistics) {
if prev_data, ok := oanda_prev[data.Symbol]; ok {
if *prev_data == *data {
return
}
}
// log.Println("oa client", data)
oanda_prev[data.Symbol] = data
select {
case ch <- data:
default:
}
}
func httpDownload(url string) (string, error) {
body, err := http.Get(url)
if err != nil {
fmt.Println(err)
return "", err
}
defer body.Body.Close()
b, err := ioutil.ReadAll(body.Body)
if err != nil {
fmt.Println(err)
return "", err
}
return string(b), nil
}
var timeregex = regexp.MustCompile("(?is)")
func match_time(html string) int64 {
result := timeregex.FindAllStringSubmatch(html, -1)
var time int64
if len(result) == 1 && len(result[0]) == 2 {
time, _ = strconv.ParseInt(result[0][1], 10, 64)
}
return time
}
var spanregex = regexp.MustCompile("(?is)]+>.*?")
func get_span(text string) []string {
result := spanregex.FindAllString(text, -1)
for i := 0; i < len(result); i++ {
result[i] = strings.Trim(strip_tags(result[i]), " \t\r\n%")
}
return result
}
var stripregex = regexp.MustCompile("(?is)<[^>]+>")
func strip_tags(text string) string {
return stripregex.ReplaceAllString(text, "")
}
var oandaInss = []string{
"EURCHF",
"XAGUSD",
"EURGBP",
"USDJPY",
"USDCAD",
"AUDJPY",
"USDCHF",
"XAUUSD",
"EURJPY",
"EURUSD",
"GBPCHF",
"AUDUSD",
"EURAUD",
"NZDUSD",
"GBPJPY",
"GBPUSD",
}
// OandaDS 实现数据源dataSource接口的定义
type OandaDS struct {
*DSBase
conf *DsConf
//insMap map[string]*market.Instrument
}
func init() {
drivers[Oanda] = newOandaDS
}
func newOandaDS(conf *DsConf) (DataSource, error) {
log.Println("newOandaDS")
ods := &OandaDS{
DSBase: NewDsBase(conf),
conf: conf,
//insMap: oandaInsMap(),
}
ods.insMap = oandaInsMap()
return ods, nil
}
func (ods *OandaDS) Name() string {
return Oanda
}
//func (ods *OandaDS) SubIns() *event.Event {
//return ods.insPublisher.Event()
//}
func (ods *OandaDS) onMarket(ps *oandaPairStatistics) {
//insId := market.OandaPrefix + ps.Symbol
insId, _ := markinfo.SymbolId(ps.Symbol)
_, ok := ods.insMap[int64(insId)]
if !ok {
log.Fatal("OandaDS.onMarket error: insId is NOT in insMap:", insId)
}
//mk := ins.GetMk()
mk := &Market{}
mk.Type = IntOanda
mk.InsId = int64(insId)
mk.Timestamp = ps.Date.Unix() * 1000
mk.LastPrice = 100 - ps.BuyPercent
mk.LastVolume = 1
//ins.SetMk(mk)
ods.Save(mk)
}
/*func (ods *OandaDS) runHour() {
ht := time.Tick(time.Hour)
for _ = range ht {
for _, ins := range ods.insMap {
ods.Save(ins.GetMk())
}
}
}*/
func (ods *OandaDS) Run() {
log.Println("OandaDS.run")
//for _, ins := range ods.insMap {
//ods.insPublisher.Publish(ins)
//}
// go ods.runHour()
//go ods.RunSave(4)
ch := make(chan *oandaPairStatistics, 1024)
go func() {
for {
ps := <-ch
ods.onMarket(ps)
}
}()
for {
var err error
for {
err = getOandaStatistics(ch)
if err != nil {
log.Println(err)
}
time.Sleep(3 * time.Second)
}
}
}
func oandaInsMap() map[int64]*Instrument {
insMap := make(map[int64]*Instrument)
for _, x := range oandaInss {
//id := market.OandaPrefix + x
id, _ := markinfo.SymbolId(x)
u, _ := markinfo.SymbolUint(x)
ins := &Instrument{
Id: int64(id),
Name: x,
ExId: market.Oanda,
Type: market.Forex,
PriceInc: u,
StartTime: time.Now().Unix() * 1000,
}
insMap[int64(id)] = ins
}
return insMap
}