ds_oanda.go 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  1. // Copyright 2013-2014 Fuzamei tech Ltd. All rights reserved.
  2. package tick
  3. // 本文件实现oanda数据源的tick数据获取下载和保存
  4. import (
  5. "errors"
  6. "fmt"
  7. "io/ioutil"
  8. "log"
  9. "net/http"
  10. "regexp"
  11. "strconv"
  12. "strings"
  13. "time"
  14. "tickserver/markinfo"
  15. "tickserver/server/market"
  16. )
  17. type oandaPairStatistics struct {
  18. Date time.Time
  19. BuyPercent float64
  20. Rates float64
  21. Symbol string
  22. }
  23. var oanda_base = int(200)
  24. var oanda_prev = make(map[string]*oandaPairStatistics)
  25. var fetchoandaurl = "http://fxtrade.oanda.com/lang/cns/analysis/open-position-ratios"
  26. func getOandaStatistics(ch chan<- *oandaPairStatistics) (err error) {
  27. html, err := httpDownload(fetchoandaurl)
  28. if err != nil {
  29. return err
  30. }
  31. regex := regexp.MustCompile("(?is)<ol[\\s]+class=\"position-ratio-list\"[^>]*>(.*?)<\\/ol>")
  32. matches := regex.FindAllStringSubmatch(html, -1)
  33. if len(matches) != 2 {
  34. return errors.New("regex matche error.")
  35. }
  36. //货币对--BuyPercent--SellPercent
  37. ratio_graph := get_span(matches[0][0])
  38. //货币对--Rates
  39. rates := get_span(matches[1][0])
  40. //精确到微妙
  41. t := match_time(html)
  42. if len(ratio_graph)%3 == 0 && len(rates)%2 == 0 && len(ratio_graph)/3 == len(rates)/2 {
  43. for i := 0; i < len(ratio_graph)/3; i++ {
  44. ops := &oandaPairStatistics{}
  45. ops.Symbol = strings.Replace(ratio_graph[3*i], "/", "", -1)
  46. ops.BuyPercent, _ = strconv.ParseFloat(ratio_graph[3*i+1], 64)
  47. ops.Rates, _ = strconv.ParseFloat(rates[2*i+1], 64)
  48. ops.Date = time.Unix(t/1000, (t%1000)*(1e6))
  49. sendOanda(ch, ops)
  50. }
  51. } else {
  52. return errors.New("invalid data.")
  53. }
  54. return
  55. }
  56. func sendOanda(ch chan<- *oandaPairStatistics, data *oandaPairStatistics) {
  57. if prev_data, ok := oanda_prev[data.Symbol]; ok {
  58. if *prev_data == *data {
  59. return
  60. }
  61. }
  62. // log.Println("oa client", data)
  63. oanda_prev[data.Symbol] = data
  64. select {
  65. case ch <- data:
  66. default:
  67. }
  68. }
  69. func httpDownload(url string) (string, error) {
  70. body, err := http.Get(url)
  71. if err != nil {
  72. fmt.Println(err)
  73. return "", err
  74. }
  75. defer body.Body.Close()
  76. b, err := ioutil.ReadAll(body.Body)
  77. if err != nil {
  78. fmt.Println(err)
  79. return "", err
  80. }
  81. return string(b), nil
  82. }
  83. var timeregex = regexp.MustCompile("(?is)<input.*?id=\"generation-timestamp\".*?value=\"(\\d+)\".*?>")
  84. func match_time(html string) int64 {
  85. result := timeregex.FindAllStringSubmatch(html, -1)
  86. var time int64
  87. if len(result) == 1 && len(result[0]) == 2 {
  88. time, _ = strconv.ParseInt(result[0][1], 10, 64)
  89. }
  90. return time
  91. }
  92. var spanregex = regexp.MustCompile("(?is)<span[^>]+>.*?</span>")
  93. func get_span(text string) []string {
  94. result := spanregex.FindAllString(text, -1)
  95. for i := 0; i < len(result); i++ {
  96. result[i] = strings.Trim(strip_tags(result[i]), " \t\r\n%")
  97. }
  98. return result
  99. }
  100. var stripregex = regexp.MustCompile("(?is)<[^>]+>")
  101. func strip_tags(text string) string {
  102. return stripregex.ReplaceAllString(text, "")
  103. }
  104. var oandaInss = []string{
  105. "EURCHF",
  106. "XAGUSD",
  107. "EURGBP",
  108. "USDJPY",
  109. "USDCAD",
  110. "AUDJPY",
  111. "USDCHF",
  112. "XAUUSD",
  113. "EURJPY",
  114. "EURUSD",
  115. "GBPCHF",
  116. "AUDUSD",
  117. "EURAUD",
  118. "NZDUSD",
  119. "GBPJPY",
  120. "GBPUSD",
  121. }
  122. // OandaDS 实现数据源dataSource接口的定义
  123. type OandaDS struct {
  124. *DSBase
  125. conf *DsConf
  126. //insMap map[string]*market.Instrument
  127. }
  128. func init() {
  129. drivers[Oanda] = newOandaDS
  130. }
  131. func newOandaDS(conf *DsConf) (DataSource, error) {
  132. log.Println("newOandaDS")
  133. ods := &OandaDS{
  134. DSBase: NewDsBase(conf),
  135. conf: conf,
  136. //insMap: oandaInsMap(),
  137. }
  138. ods.insMap = oandaInsMap()
  139. return ods, nil
  140. }
  141. func (ods *OandaDS) Name() string {
  142. return Oanda
  143. }
  144. //func (ods *OandaDS) SubIns() *event.Event {
  145. //return ods.insPublisher.Event()
  146. //}
  147. func (ods *OandaDS) onMarket(ps *oandaPairStatistics) {
  148. //insId := market.OandaPrefix + ps.Symbol
  149. insId, _ := markinfo.SymbolId(ps.Symbol)
  150. _, ok := ods.insMap[int64(insId)]
  151. if !ok {
  152. log.Fatal("OandaDS.onMarket error: insId is NOT in insMap:", insId)
  153. }
  154. //mk := ins.GetMk()
  155. mk := &Market{}
  156. mk.Type = IntOanda
  157. mk.InsId = int64(insId)
  158. mk.Timestamp = ps.Date.Unix() * 1000
  159. mk.LastPrice = 100 - ps.BuyPercent
  160. mk.LastVolume = 1
  161. //ins.SetMk(mk)
  162. ods.Save(mk)
  163. }
  164. /*func (ods *OandaDS) runHour() {
  165. ht := time.Tick(time.Hour)
  166. for _ = range ht {
  167. for _, ins := range ods.insMap {
  168. ods.Save(ins.GetMk())
  169. }
  170. }
  171. }*/
  172. func (ods *OandaDS) Run() {
  173. log.Println("OandaDS.run")
  174. //for _, ins := range ods.insMap {
  175. //ods.insPublisher.Publish(ins)
  176. //}
  177. // go ods.runHour()
  178. //go ods.RunSave(4)
  179. ch := make(chan *oandaPairStatistics, 1024)
  180. go func() {
  181. for {
  182. ps := <-ch
  183. ods.onMarket(ps)
  184. }
  185. }()
  186. for {
  187. var err error
  188. for {
  189. err = getOandaStatistics(ch)
  190. if err != nil {
  191. log.Println(err)
  192. }
  193. time.Sleep(3 * time.Second)
  194. }
  195. }
  196. }
  197. func oandaInsMap() map[int64]*Instrument {
  198. insMap := make(map[int64]*Instrument)
  199. for _, x := range oandaInss {
  200. //id := market.OandaPrefix + x
  201. id, _ := markinfo.SymbolId(x)
  202. u, _ := markinfo.SymbolUint(x)
  203. ins := &Instrument{
  204. Id: int64(id),
  205. Name: x,
  206. ExId: market.Oanda,
  207. Type: market.Forex,
  208. PriceInc: u,
  209. StartTime: time.Now().Unix() * 1000,
  210. }
  211. insMap[int64(id)] = ins
  212. }
  213. return insMap
  214. }