12
Туториалы по программированию под Betfair API. VB.Net
Jericho, Jan 20 2012 17:09
#1
20 January 2012 - 17:09
В этой теме будет вольный перевод туториалов Mumbles0'а по программированию под Betfair API со своими примерами и исходниками. В конце темы будет финальная версия бота.
Конечно повторять такие боты как GeeksToy и FairBot мы не будем, сможем конечно все вместе, но не будем. А туториалы будут полезны для многих пользователей, как некий FAQ по "ботостроительству".
Каждый отдельный пост - будет отдельный туториал...
Огромная просьба, не писать в этой теме. Комментирование, вопросы и ответы будут вестись в соседней теме - Обсуждение туториалов под Betfair API
Спасибо.
Конечно повторять такие боты как GeeksToy и FairBot мы не будем, сможем конечно все вместе, но не будем. А туториалы будут полезны для многих пользователей, как некий FAQ по "ботостроительству".
Каждый отдельный пост - будет отдельный туториал...
Огромная просьба, не писать в этой теме. Комментирование, вопросы и ответы будут вестись в соседней теме - Обсуждение туториалов под Betfair API
Спасибо.
#2
20 January 2012 - 17:52
Шаг №1. Часть №1. Подключение к сервисам BFGlobalService и BFExchangeService.
Первое, что нам пригодится для написания программы это - Betfair Sports Exchange Reference Guide v6 - открыть (pdf). Это справочник функций для доступа к сервисам BetFair API.
Все досконально описывать не буду, будем подразумевать, что читатель знаком с программированием и имеет общие понятия принципа типов данных и построения логики программы.
Создаем новый проект в VB2010 и выбираем шаблон "Windows Forms Application". Чтобы работать с Betfair API (далее API) и использовать функции описанные в справочнике, необходимо создать ссылки на сервисы:
1. Добавляем в проект сервис BFGlobalService:
006.png 8.04kb 61
Теперь мы с вами можем подключаться к бирже через API, получать рынки, делать ставки и т.д.
Первое, что нам пригодится для написания программы это - Betfair Sports Exchange Reference Guide v6 - открыть (pdf). Это справочник функций для доступа к сервисам BetFair API.
Все досконально описывать не буду, будем подразумевать, что читатель знаком с программированием и имеет общие понятия принципа типов данных и построения логики программы.
Создаем новый проект в VB2010 и выбираем шаблон "Windows Forms Application". Чтобы работать с Betfair API (далее API) и использовать функции описанные в справочнике, необходимо создать ссылки на сервисы:
1. Добавляем в проект сервис BFGlobalService:
- Открываем меню "Project"
- Выбираем "Add Service Reference"
- В текстовое поле "Address" вставляем: https://api.betfair....balService.wsdl
- Нажимаем "Go". VB2010 начнет искать сервис.
- В текстовое поле "Namespace" вместо "ServiceReference1" вводим "BFGlobal"
- Нажимаем "OK" и студия добавит в проект ссылку на сервис.
- Открываем меню "Project"
- Выбираем "Add Service Reference"
- В текстовое поле "Address" вставляем: https://api.betfair....ngeService.wsdl
- Нажимаем "Go". VB2010 начнет искать сервис.
- В текстовое поле "Namespace" вместо "ServiceReference1" вводим "BFUK"
- Нажимаем "OK" и студия добавит в проект ссылку на сервис.
006.png 8.04kb 61
Теперь мы с вами можем подключаться к бирже через API, получать рынки, делать ставки и т.д.
#3
20 January 2012 - 18:12
Шаг №1. Часть №2. Правка App.config
Забегая вперед, для тех, кто будет получать данные с помощью функции "getCompleteMarketPricesCompressed", необходимо изменить в настройках проекта размер буфера с 65536 на бОльший.
Двойным щелчком открыть App.config:
007.png 9.63kb 29
и заменить все строки с числами 65536, скажем на 999999999
Прикрепил измененный файл:
app.zip 898b 127
Все, в следующем шаге перейдем непосредственно к программированию.
Забегая вперед, для тех, кто будет получать данные с помощью функции "getCompleteMarketPricesCompressed", необходимо изменить в настройках проекта размер буфера с 65536 на бОльший.
Двойным щелчком открыть App.config:
007.png 9.63kb 29
и заменить все строки с числами 65536, скажем на 999999999
Прикрепил измененный файл:
app.zip 898b 127
Все, в следующем шаге перейдем непосредственно к программированию.
#4
20 January 2012 - 18:55
Шаг №2. Часть №1. Авторизация и сохранение сессии.
Приведем наш проект к следующему виду для удобства:
008.png 10.77kb 24
Добавим модуль и назовем его "modGlobal". Название форм, модулей, классов, переменных всегда выбирайте для себя сами по удобству и привычке...
В код модуля добавим следующие глобальные переменные:
oHeaderGL будет содержать уникальное значение текущей сессии для Betfair. BetfairGL и BetFairUK - объекты на сервис, будут содержать все вызовы и методы API. В переменной SessTokFile будем хранить путь до файла, в котором будем сохранять уникальный ключ сессии. Это нужно для того, чтобы каждый раз не логинится при тестировании программы.
Далее добавим следующие функции и процедуры:
В них собственно и проверяется и сохраняется значение сессии. Вторая процедура CheckHeader - перегрузка.
_____________
Далее добавим функцию авторизации:
Вариант 1. Пишем в коде свои логин и пароль, и просто вызываем процедуру для авторизации. Советую остановиться на этом варианте, чтобы по сто раз не их вводить.
Вариант 2. Это уже функция, через которую можно передать логин и пароль. Это для диалогового окна...
С модулем пока все...
Исходники модуля для данного шага:
modGlobal.zip 606b 127
Приведем наш проект к следующему виду для удобства:
008.png 10.77kb 24
Добавим модуль и назовем его "modGlobal". Название форм, модулей, классов, переменных всегда выбирайте для себя сами по удобству и привычке...
В код модуля добавим следующие глобальные переменные:
Public oHeaderGL As New BFGlobal.APIRequestHeader Public BetfairGL As New BFGlobal.BFGlobalServiceClient Public WithEvents BetFairUK As New BFUK.BFExchangeServiceClient Public SessTokFile As String
oHeaderGL будет содержать уникальное значение текущей сессии для Betfair. BetfairGL и BetFairUK - объекты на сервис, будут содержать все вызовы и методы API. В переменной SessTokFile будем хранить путь до файла, в котором будем сохранять уникальный ключ сессии. Это нужно для того, чтобы каждый раз не логинится при тестировании программы.
Далее добавим следующие функции и процедуры:
Public Function oHeaderUK() As BFUK.APIRequestHeader Dim Header As New BFUK.APIRequestHeader Header.sessionToken = oHeaderGL.sessionToken Return Header End Function Public Sub CheckHeader(ByVal Header As BFGlobal.APIResponseHeader) With Header oHeaderGL.sessionToken = .sessionToken End With End Sub Public Sub CheckHeader(ByVal Header As BFUK.APIResponseHeader) With Header oHeaderGL.sessionToken = .sessionToken End With End Sub
В них собственно и проверяется и сохраняется значение сессии. Вторая процедура CheckHeader - перегрузка.
_____________
Далее добавим функцию авторизации:
Вариант 1. Пишем в коде свои логин и пароль, и просто вызываем процедуру для авторизации. Советую остановиться на этом варианте, чтобы по сто раз не их вводить.
Public Sub Login() Dim oLoginReq As New BFGlobal.LoginReq Dim oLoginRes As New BFGlobal.LoginResp With oLoginReq .username = "логин" .password = "пароль" .productId = 82 End With oLoginRes = BetfairGL.login(oLoginReq) With oLoginRes CheckHeader(.header) End With End Sub
Вариант 2. Это уже функция, через которую можно передать логин и пароль. Это для диалогового окна...
Public Function LoginEx(ByVal username As String, ByVal password As String) As Integer Dim oLoginReq As New BFGlobal.LoginReq Dim oLoginRes As New BFGlobal.LoginResp With oLoginReq .username = username .password = password .productId = 82 End With oLoginRes = BetfairGL.login(oLoginReq) With oLoginRes CheckHeader(.header) End With Return 1 End Function
С модулем пока все...
Исходники модуля для данного шага:
modGlobal.zip 606b 127
#5
20 January 2012 - 19:22
Шаг №2. Часть №2. Авторизация и сохранение сессии.
Теперь перейдем к коду формы. Добавим два метода, при загрузке и закрытии формы:
При загрузке программы мы получим в переменную SessTokFile путь до нашего исполняемого файла. "\SessToken.txt" - название файла, в котором будет хранится уник. знач. сессии. Если файл есть - прочитаем это значение..
При закрытии программы сохраним значение сессии.
Файл самому создавать не нужно. Если его нет в директории - он создатся сам при закрытии формы.
___________
Ну и кидаем на форму кнопку и пишем:
При запуске программы - нажимаем на кнопку - произойдет авторизация. После чего закройте форму (нажмите на крестик) - ваша сессия сохранится, и в следующие разы "логинится" будет уже не надо.
Сколько длится сессия, в подробности не вдавался..(
Ошибки в модуле я не ловлю (такие как: неправильный пароль, блокировка аккаунта, сбой работы API и.т.д.) - это все будет в финальных исходниках...
___________
исходники формы к этому посту:
forms.zip 3.04kb 95
В следующем шаге будем получать интересности логин, баланс и.т.д..
Теперь перейдем к коду формы. Добавим два метода, при загрузке и закрытии формы:
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load SessTokFile = System.Windows.Forms.Application.StartupPath & "\SessToken.txt" oHeaderGL.sessionToken = My.Computer.FileSystem.ReadAllText(SessTokFile) End Sub Private Sub Form1_FormClosed(ByVal sender As Object, ByVal e As System.Windows.Forms.FormClosedEventArgs) Handles Me.FormClosed My.Computer.FileSystem.WriteAllText(SessTokFile, oHeaderGL.sessionToken, False) End Sub
При загрузке программы мы получим в переменную SessTokFile путь до нашего исполняемого файла. "\SessToken.txt" - название файла, в котором будет хранится уник. знач. сессии. Если файл есть - прочитаем это значение..
При закрытии программы сохраним значение сессии.
Файл самому создавать не нужно. Если его нет в директории - он создатся сам при закрытии формы.
___________
Ну и кидаем на форму кнопку и пишем:
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click Call Login() End Sub
При запуске программы - нажимаем на кнопку - произойдет авторизация. После чего закройте форму (нажмите на крестик) - ваша сессия сохранится, и в следующие разы "логинится" будет уже не надо.
Сколько длится сессия, в подробности не вдавался..(
Ошибки в модуле я не ловлю (такие как: неправильный пароль, блокировка аккаунта, сбой работы API и.т.д.) - это все будет в финальных исходниках...
___________
исходники формы к этому посту:
forms.zip 3.04kb 95
В следующем шаге будем получать интересности логин, баланс и.т.д..
#6
20 January 2012 - 19:42
Шаг №2. Часть №3. Интерфейс
Набросал примерный интерфейс будущего бота:
009.png 12.15kb 35
Тема у меня в 7ке стоит 9х... Но это на время программирования. Имхо невозможно расставлять элементы в 7ке...
Набросал примерный интерфейс будущего бота:
009.png 12.15kb 35
- (1) - выбор рынков как в Гигсе
- (2) - отображение рынков сортированных по времени
- (3) - в этой вкладке будут все ставки (сматченные/несматченные)
- (4) - баланс и др. информация.
Тема у меня в 7ке стоит 9х... Но это на время программирования. Имхо невозможно расставлять элементы в 7ке...
#7
20 January 2012 - 20:13
Шаг №2. Часть №4. Функции получения Имени пользователя и баланса аккаунта.
Откроем наш модуль и добавим следующие функции:
В переменной Currency будет содержаться валюта аккаунта. Функция GetProfile() возвращает логин пользователя, и получает валюту в переменную Currency. Функция GetAccountFunds() - возвращает баланс аккаунта.
Вызывать функции следует поочереди, например так:
Через oProfileResp можно получить множество интересных значений. Подробнее см. Betfair Sports Exchange Reference Guide v6 - viewProfile.
Откроем наш модуль и добавим следующие функции:
Public Currency As String Public Function GetProfile() As String Dim oProfileReq As New BFGlobal.ViewProfileReq Dim oProfileResp As New BFGlobal.ViewProfileResp oProfileReq.header = oHeaderGL oProfileResp = BetfairGL.viewProfile(oProfileReq) With oProfileResp CheckHeader(.header) Currency = .currency GetProfile = .userName End With End Function Public Function GetAccountFunds() As String Dim oAccountFundsReq As New BFUK.GetAccountFundsReq Dim oAccountFundsRes As New BFUK.GetAccountFundsResp oAccountFundsReq.header = oHeaderUK() oAccountFundsRes = BetFairUK.getAccountFunds(oAccountFundsReq) With oAccountFundsRes CheckHeader(.header) GetAccountFunds = Currency & .availBalance.ToString End With End Function
В переменной Currency будет содержаться валюта аккаунта. Функция GetProfile() возвращает логин пользователя, и получает валюту в переменную Currency. Функция GetAccountFunds() - возвращает баланс аккаунта.
Вызывать функции следует поочереди, например так:
Label2.Text = GetProfile() Label1.Text = GetAccountFunds()
Через oProfileResp можно получить множество интересных значений. Подробнее см. Betfair Sports Exchange Reference Guide v6 - viewProfile.
#8
20 January 2012 - 21:45
Шаг №3. Часть №1. Получение списка рынков
В этом шаге мы рассмотрим как можно получить все рынки, рынки определенного спорта, рынки за конкретный день и т.д. В последней части шага №3 у нас будет готов аналог навигатора как в GeeksToy.
1. Получаем список всех спортивных игр. Добавляем процедуру в модуль:
Добавим на форму элемент ListBox для тестирования новой функции. При вызове GetEvents() получим следующие записи:
010.png 10.82kb 23
,где .name - название вида спорта, id - соответственно id по которому мы сможем получить другие подкатегории спорта.
Как видно из списка у Скачек id -7, у Бокса - 6 и т.д. К вопросу vlad56'а как получить остальные рынки, кроме скачек. Ответ - подставить вместо 7ки другую цифру, соответсвующую id спорта... Подробнее в следующей части.
В этом шаге мы рассмотрим как можно получить все рынки, рынки определенного спорта, рынки за конкретный день и т.д. В последней части шага №3 у нас будет готов аналог навигатора как в GeeksToy.
1. Получаем список всех спортивных игр. Добавляем процедуру в модуль:
Public Sub GetEvents() Dim oEventsReq As New BFGlobal.GetEventTypesReq Dim oEventsResp As BFGlobal.GetEventTypesResp oEventsReq.header = oHeaderGL oEventsResp = BetfairGL.getActiveEventTypes(oEventsReq) With oEventsResp CheckHeader(.header) If .errorCode = BFGlobal.GetEventsErrorEnum.OK Then For i = 0 To .eventTypeItems.Length - 1 With .eventTypeItems(i) frmMain.ListBox1.Items.Add(.name & " (" & .id & ")") End With Next End If End With End Sub
Добавим на форму элемент ListBox для тестирования новой функции. При вызове GetEvents() получим следующие записи:
010.png 10.82kb 23
,где .name - название вида спорта, id - соответственно id по которому мы сможем получить другие подкатегории спорта.
Как видно из списка у Скачек id -7, у Бокса - 6 и т.д. К вопросу vlad56'а как получить остальные рынки, кроме скачек. Ответ - подставить вместо 7ки другую цифру, соответсвующую id спорта... Подробнее в следующей части.
#9
21 January 2012 - 13:36
Шаг №3. Часть №2. Получение списка рынков отдельного вида спорта
В этой части мы рассмотрим как можно получить дерево рынка конкретного спорта. Добавим в наш модуль класс MarketDataType:
Благодоря этому классу мы сможем получить всю необходимую информацию по рынку... Название, статус (закрыт, приостановлен, ин-плей..), время начала и т.д.
Добавим в модуль еще один класс UnpackAllMarkets, который в конструкторе класса будет заполнять структуру MarketDataType
Теперь мы сможем получить все рынки биржи. Добавляем следующую процедуру в содуль:
Здесь:
Ну и напоследок добавим в модуль функцию, которая заполнит нам дерево рынками:
Все. Кидаем на форму элемент TreeView и вызываем процедуру GetSoccerEvents:
Вот такое дерево получилось у меня:
013.png 19.91kb 22
Иконки стран по вкусу Можно и без них. Таким образом в GeeksToy'е и получаются отдельные виды спорта - скачки, теннис, футбол...
В седующей части прикреплю исходники проделанных шагов.
В этой части мы рассмотрим как можно получить дерево рынка конкретного спорта. Добавим в наш модуль класс MarketDataType:
Class MarketDataType Public marketId As Integer Public marketName As String Public marketType As String Public marketStatus As String Public eventDate As DateTime Public menuPath As String Public eventHeirachy As String Public betDelay As Integer Public exchangeId As Integer Public countryCode As String Public lastRefresh As DateTime Public noOfRunners As Integer Public noOfWinners As Integer Public totalAmountMatched As Double Public bspMarket As Boolean Public turningInPlay As Boolean End Class
Благодоря этому классу мы сможем получить всю необходимую информацию по рынку... Название, статус (закрыт, приостановлен, ин-плей..), время начала и т.д.
Добавим в модуль еще один класс UnpackAllMarkets, который в конструкторе класса будет заполнять структуру MarketDataType
Class UnpackAllMarkets Public marketData As MarketDataType() = {} Private Const BaseDate As DateTime = #1/1/1970# Private Const ColonCode = "&%^@" Sub New(ByVal MarketString As String) Dim n As Integer, Mdata, Field As String() Mdata = MarketString.Replace("\:", ColonCode).Split(":") n = UBound(Mdata) - 1 ReDim marketData(n) For i = 0 To n Field = Mdata(i + 1).Replace("\~", "-").Split("~") marketData(i) = New MarketDataType With marketData(i) .marketId = Field(0) .marketName = Field(1).Replace(ColonCode, ":") .marketType = Field(2) .marketStatus = Field(3) .eventDate = BaseDate.AddMilliseconds(Field(4)) .menuPath = Field(5).Replace(ColonCode, ":") .eventHeirachy = Field(6) .betDelay = Field(7) .exchangeId = Field(8) .countryCode = Field(9) .lastRefresh = BaseDate.AddMilliseconds(Field(10)) .noOfRunners = Field(11) .noOfWinners = Field(12) .totalAmountMatched = Val(Field(13)) .bspMarket = (Field(14) = "Y") .turningInPlay = (Field(15) = "Y") End With Next End Sub End Class
Теперь мы сможем получить все рынки биржи. Добавляем следующую процедуру в содуль:
Public Sub GetSoccerEvents() Dim oMarketsReq As New BFUK.GetAllMarketsReq With oMarketsReq .header = oHeaderUK() ReDim .eventTypeIds(0) : .eventTypeIds(0) = 1 .fromDate = Today .toDate = Today.AddDays(1) .locale = "UK" End With With BetFairUK.getAllMarkets(oMarketsReq) CheckHeader(.header) If .errorCode = BFUK.GetAllMarketsErrorEnum.OK Then Dim AllMarkets As New UnpackAllMarkets(.marketData) Call PopulateListView(AllMarkets, frmMain.ListView1) End If End With End Sub
Здесь:
- Функцию Call PopulateListView(AllMarkets, frmMain.ListView1), которая будет заполнять элемент ListView1 деревон рынков мы рассмотрем чуть позднее.
- .eventTypeIds(0) = 1 - означает, что "выбранный спорт" - футбол
- .fromDate = Today .toDate = Today.AddDays(1) - время рынков. У нас здесь рынки на сегодня. В GeeksToy можно выбирать "Вчера, Сегодня, Завтра" и т.д... Вот именно в эти параметры и подставляется время(дата) выбора. Т.е. от такого-то числа до такого-то...
- .locale = "UK" для рынков Королевства...
Ну и напоследок добавим в модуль функцию, которая заполнит нам дерево рынками:
Friend Sub PopulateTreeView(ByVal AllMarkets As UnpackAllMarkets, ByVal TreeView As TreeView) Dim Ids, Names As String(), Nodes As TreeNodeCollection TreeView.Nodes.Clear() With AllMarkets For i = 0 To .marketData.Length - 1 With .marketData(i) Ids = .eventHeirachy.Split("/") Names = .menuPath.Split("\") Nodes = TreeView.Nodes For j = 1 To UBound(Ids) If Not Nodes.ContainsKey(Ids(j)) Then Dim Node As New TreeNode Node.Text = If(j <= UBound(Names), Names(j), .marketName) Node.Name = Ids(j) Nodes.Add(Node) End If Nodes = Nodes(Ids(j)).Nodes Next End With Next i End With End Sub
Все. Кидаем на форму элемент TreeView и вызываем процедуру GetSoccerEvents:
Call GetSoccerEvents()
Вот такое дерево получилось у меня:
013.png 19.91kb 22
Иконки стран по вкусу Можно и без них. Таким образом в GeeksToy'е и получаются отдельные виды спорта - скачки, теннис, футбол...
В седующей части прикреплю исходники проделанных шагов.
#10
21 January 2012 - 14:01
Шаг №3. Часть №3. Исходники проекта проделанных шагов
Прикрепил к посту исходники проекта целиком с таким временным интерфейсом:
014.png 18.44kb 21
Test_BF.zip 134.52kb 168
Прикрепил к посту исходники проекта целиком с таким временным интерфейсом:
014.png 18.44kb 21
Test_BF.zip 134.52kb 168
#11
21 January 2012 - 16:29
Шаг №4. Часть №1. Строим навигатор как в GeeksToy
Открываем проект из Шага №3, и в код формы добавляем следующий код:
Тем самым мы получим следующий вид:
016.png 2.58kb 9
Вместо приведенного кода можно добавить итемы и через Properties(свойства) элемента..
По умолчанию у нас будут показываться все футбольные рынки на сегодняшний день.
Открываем проект из Шага №3, и в код формы добавляем следующий код:
Private Sub frmMain_Invalidated(ByVal sender As Object, ByVal e As System.Windows.Forms.InvalidateEventArgs) Handles Me.Invalidated ComboBox1.Items.Add("Horses") ComboBox1.Items.Add("Soccer") : ComboBox1.SelectedIndex = 1 ComboBox1.Items.Add("Tennis") ComboBox2.Items.Add("Yesterday") ComboBox2.Items.Add("Today") : ComboBox2.SelectedIndex = 1 ComboBox2.Items.Add("Tomorrow") ComboBox3.Items.Add("All") : ComboBox3.SelectedIndex = 0 ComboBox3.Items.Add("In-play") ComboBox3.Items.Add("On Now") End Sub
Тем самым мы получим следующий вид:
016.png 2.58kb 9
Вместо приведенного кода можно добавить итемы и через Properties(свойства) элемента..
По умолчанию у нас будут показываться все футбольные рынки на сегодняшний день.
#12
21 January 2012 - 17:12
Шаг №4. Часть №1. Строим навигатор как в GeeksToy
Далее добавим в код формы реакцию на выбор ComboBox'ов:
Переменные selSport, selTime, selType, объявим в модуле:
И подправим функцию GetSoccerEvents на следующую процедуру:
Теперь при выборе футбола - получим футбольные рынки, при лошадках - лошадиные) и т.д...
017.png 5.82kb 15
Далее добавим в код формы реакцию на выбор ComboBox'ов:
Private Sub ComboBox1_SelectedValueChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles ComboBox1.SelectedValueChanged selSport = ComboBox1.SelectedItem End Sub Private Sub ComboBox2_SelectedValueChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles ComboBox2.SelectedValueChanged selTime = ComboBox2.SelectedItem End Sub Private Sub ComboBox3_SelectedValueChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles ComboBox3.SelectedValueChanged selType = ComboBox3.SelectedItem End Sub
Переменные selSport, selTime, selType, объявим в модуле:
Public selSport As String, selTime As String, selType As String
И подправим функцию GetSoccerEvents на следующую процедуру:
Public Sub GetEventsEx() Dim eventTypeIds As Integer, fromDate, toDate As Date Select Case selSport Case "Horses" : eventTypeIds = 7 Case "Soccer" : eventTypeIds = 1 Case "Tennis" : eventTypeIds = 2 End Select Select Case selTime Case "Yesterday" fromDate = Today.AddDays(-1) toDate = Today Case "Today" fromDate = Today toDate = Today.AddDays(1) Case "Tomorrow" fromDate = Today.AddDays(1) toDate = Today.AddDays(2) End Select Dim oMarketsReq As New BFUK.GetAllMarketsReq With oMarketsReq .header = oHeaderUK() ReDim .eventTypeIds(0) : .eventTypeIds(0) = eventTypeIds .fromDate = fromDate .toDate = toDate .locale = "UK" End With With BetFairUK.getAllMarkets(oMarketsReq) CheckHeader(.header) If .errorCode = BFUK.GetAllMarketsErrorEnum.OK Then Dim AllMarkets As New UnpackAllMarkets(.marketData) Call PopulateTreeView(AllMarkets, frmMain.TreeView1) End If End With End Sub
Теперь при выборе футбола - получим футбольные рынки, при лошадках - лошадиные) и т.д...
017.png 5.82kb 15
#13
24 January 2012 - 20:27
Дополнение. №1
В теме "Запланированное и не очень отключение серверов Betfair" говорилось о боте, который "поможет" узнать какие технические работы ведутся на бирже, и когда это кончится...
Пример сегодняшних работ для английской аудитории:
031.png 8.18kb 23
______________
Немного теории как это все получить:
Чтобы увидеть ведутся ли технические работы на сервере - достаточно пройти по адресу: service.betfair.info/index.html
Открыв исходники html страницы, мы увидим следующее:
032.png 4.4kb 21
В файле englishfrom1.js содержится строка даты и времени, когда планируется отключение (25/01/2012, 6:00AM), соответственно englishto1.js - время и дата окончания работ, englishproduct1.js - вид работ...
В имени файла, например englishfrom1.js - english означает страну, для которой отображать время (для России - russianfrom1.js), а цифра 1 - означает строку работ. Как видно строк с работами 3, значит и фалов должно быть три...
______________
Теперь давайте получим это все программно...
1. На форме разместим элемент ListView:
034.png 9.4kb 12
и в коде формы у нас должно быть следующее:
Теперь разберем по функциям:
1. Private Sub Form1_Invalidated.... - начинает работу при запуске программы.В ней через цикл Do... Loop заполняются строки элемента ListView. Если технических работ на сервере нет - мы получим пустой список.
2. GetContents - функция возвращает строку между скобками и удаляет все лишнее. Если, например есть строка блаблабла(инфа)бла... то функция возвратит - инфа
3. GetErr - функция через web_client.OpenRead получает содержимое тех самых файлов, описаных выше (englishfrom1.js) и возвращает через GetContents чистую нужную строку с информацией...
В итоге мы видим следующую картину:
033.png 11kb 11
Для России в Private Sub Form1_Invalidated заменить
Мне очень помогает... Написал этот код сразу же, после того как попал после незапланированных тех. работ...
Хотя если ставка уже принята, и внезапно пошли сбои в работе сервера, будет уже поздно... но проверить лишний раз никогда не мешает.
В теме "Запланированное и не очень отключение серверов Betfair" говорилось о боте, который "поможет" узнать какие технические работы ведутся на бирже, и когда это кончится...
Пример сегодняшних работ для английской аудитории:
031.png 8.18kb 23
______________
Немного теории как это все получить:
Чтобы увидеть ведутся ли технические работы на сервере - достаточно пройти по адресу: service.betfair.info/index.html
Открыв исходники html страницы, мы увидим следующее:
032.png 4.4kb 21
В файле englishfrom1.js содержится строка даты и времени, когда планируется отключение (25/01/2012, 6:00AM), соответственно englishto1.js - время и дата окончания работ, englishproduct1.js - вид работ...
В имени файла, например englishfrom1.js - english означает страну, для которой отображать время (для России - russianfrom1.js), а цифра 1 - означает строку работ. Как видно строк с работами 3, значит и фалов должно быть три...
______________
Теперь давайте получим это все программно...
1. На форме разместим элемент ListView:
034.png 9.4kb 12
и в коде формы у нас должно быть следующее:
Imports System.IO Public Class Form1 Private Sub Form1_Invalidated(ByVal sender As Object, ByVal e As System.Windows.Forms.InvalidateEventArgs) Handles Me.Invalidated Dim objItem As ListViewItem Try Dim i As Integer = 1 Do objItem = ListView1.Items.Add(GetErr("englishfrom", i)) objItem.SubItems.Add(GetErr("englishto", i)) objItem.SubItems.Add(GetErr("englishproduct", i)) i = i + 1 Loop Catch ex As Exception 'MsgBox("Err") End Try End Sub Function GetContents(ByRef strSource As String) As String Dim strStart, strEnd As Integer, strLen As Integer strStart = InStr(strSource, "(") + 2 strEnd = InStr(strSource, ")") - 1 strLen = strEnd - strStart Return Mid(strSource, strStart, strLen) End Function Function GetErr(ByVal str As String, ByVal i As Integer) As String Dim web_client As New Net.WebClient() Dim response As Stream response = web_client.OpenRead("http://service.betfair.info/files/frames/" & str & i & ".js") Dim stream_reader As New IO.StreamReader(response) Return GetContents(stream_reader.ReadToEnd()) : stream_reader.Close() End Function End Class
Теперь разберем по функциям:
1. Private Sub Form1_Invalidated.... - начинает работу при запуске программы.В ней через цикл Do... Loop заполняются строки элемента ListView. Если технических работ на сервере нет - мы получим пустой список.
2. GetContents - функция возвращает строку между скобками и удаляет все лишнее. Если, например есть строка блаблабла(инфа)бла... то функция возвратит - инфа
3. GetErr - функция через web_client.OpenRead получает содержимое тех самых файлов, описаных выше (englishfrom1.js) и возвращает через GetContents чистую нужную строку с информацией...
В итоге мы видим следующую картину:
033.png 11kb 11
Для России в Private Sub Form1_Invalidated заменить
objItem = ListView1.Items.Add(GetErr("englishfrom", i)) objItem.SubItems.Add(GetErr("englishto", i)) objItem.SubItems.Add(GetErr("englishproduct", i))на
objItem = ListView1.Items.Add(GetErr("russianfrom", i)) objItem.SubItems.Add(GetErr("russianto", i)) objItem.SubItems.Add(GetErr("russianproduct", i))
Мне очень помогает... Написал этот код сразу же, после того как попал после незапланированных тех. работ...
Хотя если ставка уже принята, и внезапно пошли сбои в работе сервера, будет уже поздно... но проверить лишний раз никогда не мешает.
#14
28 January 2012 - 19:10
Шаг №5. Часть№1. Получаем всю информацию о рынке(коэф-ты, суммы, графики и.т.д.)
Прежде чем приступить к новому шагу - небольшое изменение к Дополнению. №1.
Чтобы исключить тех. работы, которые проводились раннее - необходимо поставить в цикл проверку сегодняшнего дня: If GetErr("englishfrom", i) = Today Then:
____________
Определяем ID рынка
После того, как мы прошли все четыре шага, у нас есть дерево, которое заполнено спортивными событиями. Пока только будем рассматривать футбол(особой разницы между спортом нет... Единственное в скачках больше информации о рынке).
У каждого элемента дерева есть ключ - в эти ключи в функции PopulateTreeView(AllMarkets, frmMain.TreeView1)
мы занесли id событий и рынков(всю иерархию).
Зная id каждого рынка мы сможем получить о нем всю возможную информацию...
Добавим для наглядности на форму два элемента TextBox.. И поместим в код формы следующие строки:
Данная процедура будет выполняться после "выбора" элемента дерева(клик или перемещение клавишами).
Запускаем проект... И теперь перемещаясь по дереву, если элемент в иерархии не конечный:
039.png 8.76kb 17
038.png 12.05kb 14
Прежде чем приступить к новому шагу - небольшое изменение к Дополнению. №1.
Чтобы исключить тех. работы, которые проводились раннее - необходимо поставить в цикл проверку сегодняшнего дня: If GetErr("englishfrom", i) = Today Then:
Do If GetErr("englishfrom", i) = Today Then objItem = ListView1.Items.Add(GetErr("englishfrom", i)) objItem.SubItems.Add(GetErr("englishto", i)) objItem.SubItems.Add(GetErr("englishproduct", i)) End If i = i + 1 Loop
____________
Определяем ID рынка
После того, как мы прошли все четыре шага, у нас есть дерево, которое заполнено спортивными событиями. Пока только будем рассматривать футбол(особой разницы между спортом нет... Единственное в скачках больше информации о рынке).
У каждого элемента дерева есть ключ - в эти ключи в функции PopulateTreeView(AllMarkets, frmMain.TreeView1)
мы занесли id событий и рынков(всю иерархию).
Зная id каждого рынка мы сможем получить о нем всю возможную информацию...
Добавим для наглядности на форму два элемента TextBox.. И поместим в код формы следующие строки:
Private Sub TreeView1_AfterSelect(ByVal sender As System.Object, ByVal e As System.Windows.Forms.TreeViewEventArgs) Handles TreeView1.AfterSelect TextBox2.Text = Val(e.Node.Name) If e.Node.Nodes.Count = 0 Then Beep() Dim marketId As Integer = Val(e.Node.Name) TextBox1.Text = marketId & "-" & e.Node.Text TextBox2.Text = e.Node.FullPath End If End Sub
Данная процедура будет выполняться после "выбора" элемента дерева(клик или перемещение клавишами).
Запускаем проект... И теперь перемещаясь по дереву, если элемент в иерархии не конечный:
039.png 8.76kb 17
- В TextBox2 - мы увидим Id элемента иерархии событий на бирже...
038.png 12.05kb 14
- В TextBox1 - id рынка и через тире его название
- В TextBox2 - "путь" элементов
#15
28 January 2012 - 19:38
Шаг №5. Часть№2. Получаем всю информацию о рынке(коэф-ты, суммы, графики и.т.д.)
Сейчас приведу кусок кода, а потом мы его рассмотрим очень подробно
Думаю, читатель, у которого хватило терпения дочитать до этого шага уже знает, что такое процедуры и функции, принимаемые и передаваемые переменные... Поэтому далее будут только функции, которые "делают" хорошие вещи), и как их вызвать...
Следующая функция получит коэф-ты первой строчки Back и Lay того рынка, которой мы укажем через Id:
Быстренько по функции
Вызов функции:
В следующей части рассмотрим что же за параметры такие runnerPrices(0), bestPricesToBack(0) и другие непонятные слова.
Сейчас приведу кусок кода, а потом мы его рассмотрим очень подробно
Думаю, читатель, у которого хватило терпения дочитать до этого шага уже знает, что такое процедуры и функции, принимаемые и передаваемые переменные... Поэтому далее будут только функции, которые "делают" хорошие вещи), и как их вызвать...
Следующая функция получит коэф-ты первой строчки Back и Lay того рынка, которой мы укажем через Id:
Public Function GetOdds(ByVal id As String, ByRef Back As String, ByRef Lay As String) As Boolean Dim oMarketReq As New BFUK.GetMarketPricesReq Dim oMarketResp As BFUK.GetMarketPricesResp With oMarketReq .header = oHeaderUK() .marketId = id End With oMarketResp = BetFairUK.getMarketPrices(oMarketReq) With oMarketResp CheckHeader(.header) If .errorCode = BFUK.GetMarketPricesErrorEnum.OK Then With .marketPrices With .runnerPrices(0) With .bestPricesToBack(0) Back = .price End With With .bestPricesToLay(0) Lay = .price End With End With End With End If End With Return True End Function
Быстренько по функции
- в oMarketReq передаем id рынка (помните, мы в предыдущем шаге научились его получать...)
- получаем огромную структуру данных в oMarketResp
Вызов функции:
Dim Lay, Back As String GetOdds(id, Back, Lay)
- в id должен быть идентификатор рынка
- а в переменных Lay и Back будут коэф-ты соответственно...
В следующей части рассмотрим что же за параметры такие runnerPrices(0), bestPricesToBack(0) и другие непонятные слова.
#16
28 January 2012 - 21:16
Шаг №5. Часть№3. Получаем всю информацию о рынке(коэф-ты, суммы, графики и.т.д.)
Проведем аналогию GeeksToy'ем и поймем что же за информацию мы можем получить в oMarketResp.
1. .runnerPrices() - если дословно, массив забегов (скорее всего пошло из скачек)... По-нашему событий.
040.png 21.58kb 12
1. .bestPricesToBack() и .bestPricesToLay(0) - массив коэф-тов на Back и Lay. Отсчет идет, как показано на рисунке:
040.png 25.68kb 15
2. .selectionId - id выбранного нами .runnerPrices(). Впоследствии пригодится нам для получения графика...
3. .totalAmountMatched - сумма сматченных денег выбранного нами .runnerPrices(). (показана зеленым прямоугольником)
040.png 24.96kb 14
040.png 25.23kb 20
1..price и .amountAvailable - коэф. и сумма соответсвенно для выбранного stPricesToBack() или .bestPricesToLay()
040.png 24.92kb 9
Чтобы более было понятно к середине дня скину функцию, которая получит по всю возможную информацию...
Проведем аналогию GeeksToy'ем и поймем что же за информацию мы можем получить в oMarketResp.
1. .runnerPrices() - если дословно, массив забегов (скорее всего пошло из скачек)... По-нашему событий.
040.png 21.58kb 12
- Видим на скрине, что Ювентус - это .runnerPrices(0), Удинезе - .runnerPrices(1) и т.д. Массив может быть разной размерности.. Здесь три, на рынках тоталов - два, на рынках "Результат(CorrectScore)" - куча...
- Кол-во этих "забегов" можно взять из marketData(i) из noOfRunners (Шаг №3. Часть №2. Получение списка рынков отдельного вида спорта).
1. .bestPricesToBack() и .bestPricesToLay(0) - массив коэф-тов на Back и Lay. Отсчет идет, как показано на рисунке:
040.png 25.68kb 15
2. .selectionId - id выбранного нами .runnerPrices(). Впоследствии пригодится нам для получения графика...
3. .totalAmountMatched - сумма сматченных денег выбранного нами .runnerPrices(). (показана зеленым прямоугольником)
040.png 24.96kb 14
- Общая сматченная сумма(показана красным прямоугольником) получается путем сложения всех .totalAmountMatched.
040.png 25.23kb 20
- Красным прямоугольником помечен последний сматченный коэф
- Зеленым - сумма. Сумма вычисляется путем запоминания .totalAmountMatched в переменную. А после того, как она изменилась - вычитается из сохраненной. Результат и будет последняя сматченная сумма.
1..price и .amountAvailable - коэф. и сумма соответсвенно для выбранного stPricesToBack() или .bestPricesToLay()
040.png 24.92kb 9
Чтобы более было понятно к середине дня скину функцию, которая получит по всю возможную информацию...
#17
28 January 2012 - 21:35
Шаг №5. Часть№3. Получаем всю информацию о рынке(коэф-ты, суммы, графики и.т.д.)
1. Получение графика.
Следующая процедура загружает график интересуещего нас события.
Необходимо расположить на форме элемент PictureBox1 и вызвать следующую процидуру:
Функция загружает в массив байт необходимую картинку с графиком с сервера биржи, и рисует ее в элементе PictureBox1:
041.png 76.59kb 7
В следующем шаге рассмотрим понятие Многопото́чность и научимся получать много данных одновремнно "без тормозов" главной формы программы.
1. Получение графика.
Следующая процедура загружает график интересуещего нас события.
Необходимо расположить на форме элемент PictureBox1 и вызвать следующую процидуру:
Private Sub GetGraph(ByVal MarketId As String, ByVal SelectionId As String) Dim MyWebClient As New System.Net.WebClient Dim ImageInBytes() As Byte = MyWebClient.DownloadData("http://uk.site.sports.betfair.com/betting/LoadRunnerInfoChartAction.do?marketId=" & MarketId & "&selectionId=" & SelectionId & "&asianLineId=0") Dim ImageStream As New IO.MemoryStream(ImageInBytes) PictureBox1.Image = New System.Drawing.Bitmap(ImageStream) End Sub
- В параметр ByVal MarketId передать id рынка (Шаг №5. Часть№1.)
- В параметр ByVal SelectionId передать id события (Шаг №5. Часть№2 - Часть№3.)
Функция загружает в массив байт необходимую картинку с графиком с сервера биржи, и рисует ее в элементе PictureBox1:
041.png 76.59kb 7
В следующем шаге рассмотрим понятие Многопото́чность и научимся получать много данных одновремнно "без тормозов" главной формы программы.
#18
10 February 2012 - 19:12
Шаг №6. Часть№1. Многопоточность
Если мы с вами захотим получить много информации с разных рынков, то наша программа "подвиснет" на момент получения и обработки данных, пока функция не будет закончена. Чтобы этого избежать можно использовать фоновый поток для нашего приложения.
Многопоточность - свойство платформы (например, операционной системы, виртуальной машины и т. д.) или приложения, состоящее в том, что процесс, порождённый в операционной системе, может состоять из нескольких потоков, выполняющихся «параллельно», то есть без предписанного порядка во времени.
Чтобы облегчить нам жизнь существует такой компонент, как BackgroundWorker, он существует начиная с .NET Framework 2.0. Компонент позволяет производить некоторую операцию в отдельном потоке, например это бывает необходимо при долгих вычислениях, или множественных и продолжительных запросах к базе данных, сервисам и т.д..
У компонента всего три события:
Сделаем получение коэф. из Шаг №5. Часть№2. в отдельном потоке:
1. Добавим структуру, которую и заполним в новом потоке информацией о рынке:
Структурку делал под себя - у вас она может быть сколь угодно разной(можете хоть всю информацию о рынке выжать в подобную структуру)..
Содержание структуры:
2. Вставим следующий код, например в событие кнопки:
В этом коде:
По коду:
Код из Шаг №5. Часть№2, только данные о рынке загружаются в массив mOdds.
4. Процедура отображения "прогресса". Добавим на форму элемент ProgressBar1
5. При завершении потока:
Здесь мы заолняем строки данными из массива mOdds
Таким образом можно разделить задачи в одной программе.
Если мы с вами захотим получить много информации с разных рынков, то наша программа "подвиснет" на момент получения и обработки данных, пока функция не будет закончена. Чтобы этого избежать можно использовать фоновый поток для нашего приложения.
Многопоточность - свойство платформы (например, операционной системы, виртуальной машины и т. д.) или приложения, состоящее в том, что процесс, порождённый в операционной системе, может состоять из нескольких потоков, выполняющихся «параллельно», то есть без предписанного порядка во времени.
Чтобы облегчить нам жизнь существует такой компонент, как BackgroundWorker, он существует начиная с .NET Framework 2.0. Компонент позволяет производить некоторую операцию в отдельном потоке, например это бывает необходимо при долгих вычислениях, или множественных и продолжительных запросах к базе данных, сервисам и т.д..
У компонента всего три события:
- DoWork - "работа", которую будет делать поток
- ProgressChanged - отображает изменение потока
- RunWorkerCompleted - действия по окончании потока
Сделаем получение коэф. из Шаг №5. Часть№2. в отдельном потоке:
1. Добавим структуру, которую и заполним в новом потоке информацией о рынке:
Public Structure MarketOdds Dim MarketId As String Dim b0 As String Dim b0Amount As String Dim l0 As String Dim l0Amount As String Dim tAmount As String Dim SelId As String End Structure Public mOdds() As MarketOdds = {}
Структурку делал под себя - у вас она может быть сколь угодно разной(можете хоть всю информацию о рынке выжать в подобную структуру)..
Содержание структуры:
- MarketId - id рынка
- b0 - текущий коэф. back рынка
- b0Amount - сумма под коэф. back
- l0 - текущий коэф. lay рынка
- l0Amount - сумма под коэф. lay
- tAmount - сматченная сумма рынка
- SelId - id выбранного рынка (например ТМ или ТБ)
2. Вставим следующий код, например в событие кнопки:
ReDim mOdds(ListView1.Items.Count - 1) For i As Integer = 0 To ListView1.Items.Count - 1 mOdds(i).MarketId = ListView1.Items(i).SubItems(6).Text Next BackgroundWorker1.RunWorkerAsync()
В этом коде:
- ReDim mOdds(ListView1.Items.Count - 1) - для начала нам нужно указать размерность массива нашей структуры. В данной ситуации размерность равна кол-ву элементов в контроле ListView1. В строках ListView1 я храню id рынка. (как получить id рынков - Шаг №3. Часть №2.)
- А далее присваиваем каждому элементу массива mOdds(i).MarketId свой id
- BackgroundWorker1.RunWorkerAsync() - запускаем поток
Private Sub BackgroundWorker1_DoWork(ByVal sender As System.Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork Dim oMarketReq As New BFUK.GetMarketPricesReq Dim oMarketResp As BFUK.GetMarketPricesResp For i As Integer = 0 To UBound(mOdds) With oMarketReq .header = oHeaderUK() .marketId = mOdds(i).MarketId End With oMarketResp = BetFairUK.getMarketPrices(oMarketReq) With oMarketResp CheckHeader(.header) If .errorCode = BFUK.GetMarketPricesErrorEnum.OK Then With .marketPrices With .runnerPrices(0) With .bestPricesToBack(0) mOdds(i).b0 = .price mOdds(i).b0Amount = .amountAvailable End With With .bestPricesToLay(0) mOdds(i).l0 = .price mOdds(i).l0Amount = .amountAvailable End With mOdds(i).tAmount = .totalAmountMatched.ToString mOdds(i).SelId = .selectionId End With End With End If End With System.Threading.Thread.Sleep(500) If ListView1.Items.Count = 1 Then Else BackgroundWorker1.ReportProgress(CInt((i / (ListView1.Items.Count - 1)) * 100)) End If Next End Sub
По коду:
Код из Шаг №5. Часть№2, только данные о рынке загружаются в массив mOdds.
- В каждом шаге цикла есть строка - System.Threading.Thread.Sleep(500) - заставляет поток "заснуть" на 500 милисекунд... Чтобы ограничить кол-во запросов к бирже.
- BackgroundWorker1.ReportProgress(CInt((i / (ListView1.Items.Count - 1)) * 100)) - передает процент выполнения задачи...
4. Процедура отображения "прогресса". Добавим на форму элемент ProgressBar1
Private Sub BackgroundWorker1_ProgressChanged(ByVal sender As Object, ByVal e As System.ComponentModel.ProgressChangedEventArgs) Handles BackgroundWorker1.ProgressChanged Me.ProgressBar1.Value = e.ProgressPercentage End Sub
5. При завершении потока:
Private Sub BackgroundWorker1_RunWorkerCompleted(ByVal sender As Object, ByVal e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted For i As Integer = 0 To ListView1.Items.Count - 1 ListView1.Items(i).SubItems(2).Text = mOdds(i).b0 ListView1.Items(i).SubItems(3).Text = mOdds(i).l0 Next End Sub
Здесь мы заолняем строки данными из массива mOdds
Таким образом можно разделить задачи в одной программе.
#19
08 July 2012 - 23:20
Jericho, Вы позволите получить весь бот целиком. Я не уверена, что сделала ве правильно. VB только начинаю осваивать. Есть предположение. Хочу проверить. Буду признательна.
#20
09 July 2012 - 13:38
Jericho, Вы позволите получить весь бот целиком. Я не уверена, что сделала ве правильно. VB только начинаю осваивать. Есть предположение. Хочу проверить. Буду признательна.
Леночка, я этого бота уже не найду наверно у себя. Были проблемы с железом.
Готовый мой бот есть здесь. Его будет достаточно для вашей проверки. В нем есть все функции описанные в этих туториалах.