понедельник, 1 октября 2012 г.

Betfair Free API. Ч.4. Продолжаем создавать бот.


Продолжаем создавать бот для автоматического доступа к бирже Betfair. В конце концов, в последней части, у нас получится полностью законченный бот. Что же он будет делать? Ну, допустим, пусть он будет расставлять минимальную  ставку LAY с коэффициентом 1.01 на все вновь появившиеся теннисные рынки.

Итак, в это части добавим в бот, который уже умеет логиниться к бирже, способность получать новые рынки. Внизу измененный код, из предыдущего примера, который умеет это делать. Изменить следует основной фал исходного кода программы JavaApplication1.java (не забудьте подставить свой логин и пароль к бирже в строках 27 и 28)


package javaapplication1;

import com.betfair.publicapi.types.exchange.v5.ArrayOfInt;
import com.betfair.publicapi.types.exchange.v5.GetAllMarketsReq;
import com.betfair.publicapi.types.exchange.v5.GetAllMarketsResp;
import com.betfair.publicapi.types.global.v3.LoginReq;
import com.betfair.publicapi.types.global.v3.LoginResp;
import com.betfair.publicapi.v3.bfglobalservice.BFGlobalService;
import com.betfair.publicapi.v3.bfglobalservice.BFGlobalService_Service;
import com.betfair.publicapi.v5.bfexchangeservice.BFExchangeService;
import com.betfair.publicapi.v5.bfexchangeservice.BFExchangeService_Service;
import java.util.ArrayList;

public class JavaApplication1 {

    public static void main(String[] args) {
            String sessionToken = "";
            com.betfair.publicapi.types.exchange.v5.APIRequestHeader exchangeHeader = new com.betfair.publicapi.types.exchange.v5.APIRequestHeader();
            
            BFGlobalService_Service WSDLService_Global = new BFGlobalService_Service();
            BFGlobalService WSDLPort_Global = WSDLService_Global.getBFGlobalService();

            BFExchangeService_Service WSDLService_Exchange = new BFExchangeService_Service();
            BFExchangeService WSDLPort_Exchange = WSDLService_Exchange.getBFExchangeService();
            
            LoginReq loginRequest = new LoginReq();
            loginRequest.setUsername("mylogToBetfair");
            loginRequest.setPassword("mypassword");
            loginRequest.setProductId(82);
            loginRequest.setVendorSoftwareId(0);
            loginRequest.setIpAddress("0");
            loginRequest.setLocationId(0);
            
            LoginResp result = WSDLPort_Global.login(loginRequest);
            
            System.out.println("Логин на Betfair - " + result.getErrorCode().toString());
            if (result.getErrorCode().toString().equals("OK")) {
                sessionToken = result.getHeader().getSessionToken();
                System.out.println("Текущий session Token - " + result.getHeader().getSessionToken());
            } else {
                // По какой-то причине логин не получился. Выход из программы
                System.exit(0);
            }
            
            /*********** Готовим запрос и запрашиваем все теннисные рынки **************/
            
            // Готовим заголовок для запроса
            exchangeHeader.setSessionToken(sessionToken);
            exchangeHeader.setClientStamp(0);
            
            // Готовим запрос всех теннисных рынков
            GetAllMarketsReq allMarketsRequest = new GetAllMarketsReq();
            // Добавляем в запрос заголовок запроса
            allMarketsRequest.setHeader(exchangeHeader);
            allMarketsRequest.setLocale("en");
            // Создаем массив интересущих нас видов спорта
            ArrayOfInt array = new ArrayOfInt();
            // Добавляем в этот массив число 2 соответствующее теннису
            // Например, для футбола это 1.
            // Соответствие чисел видам спорта смотрим здесь http://data.betfair.com/sportids.htm?rfr=15929
            array.getInt().add(2);
            // Подставляем в массив видов спорта в запрос
            allMarketsRequest.setEventTypeIds(array);
            
            // Запрос готов! Отправляем его на сайт Betfair
            GetAllMarketsResp resp = WSDLPort_Exchange.getAllMarkets(allMarketsRequest);

            System.out.println("Ответ на запрос рынков - " + resp.getErrorCode().toString());
            // Проверяем ответ. Сравниваем его со строкой ОК
            if (!resp.getErrorCode().toString().equals("OK")) {
                // Запрос завершился неудачно. Выходим из программы
                System.out.println("Ответ на запрос рынков - " + resp.getErrorCode().toString());
                System.exit(0);
            }
            // Разбираем весь запрос.
            ArrayList<MyMarketInfo>> markets = MarketsParser.parseMarkets(resp.getMarketData());
            if (markets.isEmpty()) {
                System.out.println("Не получено ни одного рынка в ответе на запрос рынков");
                System.exit(0);
            } else {
                System.out.println("Получено всего " + markets.size() + " рынков в ответе на запрос рынков");
            }
            
            // Из всего множества теннисных рынков формируем массив
            // который будет содержать только рынки Match Odds (ставки на победителя в матче)
            ArrayLis<MyMarketInfo> matchOddsMarkets = new ArrayList<MyMarketInfo>();
            for (int i = 0; i < markets.size(); i++) {
                if (markets.get(i).getMarketName().equals("Match Odds")) {
                    matchOddsMarkets.add(markets.get(i));
                }
            }
            // Печатаем количество Match Odds рынков или выходим из программы если таких рынков не обнаружено
            if (matchOddsMarkets.isEmpty()) {
                System.out.println("Не найдено ни одного Match Odds рынка");
                System.exit(0);
            } else {
                System.out.println("Получено всего " + matchOddsMarkets.size() + " Macth Odds рынков в ответе на запрос теннисных рынков");
            }
            
            // Печатаем все полученные рынки на экран
            for (int i = 0; i < matchOddsMarkets.size(); i++) {
                System.out.println((i+1) + ". " + matchOddsMarkets.get(i).getEventName() + ", " + matchOddsMarkets.get(i).getMarketMenuPath());
            }
    }
}

class MarketsParser {
}

class MyMarketInfo {
}

Пробежимся по коду.

Строки 48, 49. Готовим заголовок для запроса рынков. Подставляем в заголовок сессионный ключ полученный в ответе на логин. Это делается для того что бы не надо было в каждом запросе вставлять свой логин и пароль.

Строки 52-63. Готовим запрос к бирже для получения всех возможных теннисных рынков. То, что нас интересуют только теннисные рынки указано в строке 61. Об этом говорит число 2. Если б нас интересовал еще и футбол, то надо было бы написать в следующей строке  array.getInt().add(2). У каждого вида спорта есть свое число которое надо подставлять в запрос. Список всех возможных чисел смотрим здесь.

Строка 66. Отправляем, только что сформированный запрос на биржу.

Строки 68-74. Проверяем код ошибки и завершаем программу если код ошибки отличается от строки "ОК".

Строки 76-82. Разбираем ответ биржи. Внимание! Ответ от биржи будет получен в виде одной строки следующего уродливого вида -
106955486~Match Odds~O~ACTIVE~1349238600000~\Tennis\Group A\China Open 2012\Womens Tournament\Second Round Matches\Bartoli v Morita~/2/26900942/26901463/26901449/26901677/106955486~0~1~CHN~1349038369954~2~1~0.0~N~Y:106955487~Set Betting~O~ACTIVE~1349238600000~\Tennis\Group A\China Open 2012\Womens Tournament\Second Round Matches\Bartoli v Morita~/2/26900942/26901463/26901449/26901677/106955487~0~1~CHN~1349038369954~4~1~0.0~N~Y
Новичку в программировании поначалу сложно будет извлечь полезную информацию из этой информационной свалки. Поэтому я облегчу ваши страдания. Я написал специальный класс который будет за мгновение ока разбирать эту строку и создавать отдельный объект типа MyMarketInfo для каждого найденного рынка. Что бы не загромождать страницу я не привел описания этих классов здесь. Однако, я приложу весь исходный текст программы целиком в конце этой заметки. Вам надо будет только скопипасить его на место кода написанного в предыдущем посте.

Строки 86-91. Фильтруем все найденные рынки и отбираем рынки которые называются Match Odds. Что соответствует обычным ставкам на победителя в теннисном матче. Собираем отфильтрованные рынки в переменной массиве matchOddsMarkets.

Строки 92-103. Распечатываем количество полученных теннисных рынков, а также все Match Odds рынки  с названиями рынков.

Весь исходный текст программы в одном файле JavaApplication1.java



16 комментариев:

  1. Спасибо за пост. Ты монстр!!! А почему именно выбрал Java?

    ОтветитьУдалить
  2. Тема многим интересна. У некоторых даже руки дойдут до реализации. Надеюсь и у меня тоже, когда будет больше времени )
    Спасибо за посты!

    ОтветитьУдалить
  3. Спасибо за тему, автор!
    Я вот из-за желания написать бота взялся с нуля учить сишарп. А тут такой доступный материал на Яве...Просьба поделиться соображениями с точки зрения выбора языка.

    ОтветитьУдалить
  4. Яву я выбрал в качестве языка по причине того, что язык этот намного проще чем С#/C++ и непрограммисту освоить его намного легче.

    ОтветитьУдалить
  5. Привет! Как то читал в твоем блоге, что ты хочешь перебраться на Гоа, у меня тоже есть такая задумка, только на зиму,летом там дожди и говорят делать там нечего. Хотел бы узнать там можно найти недорогое жилье с инетом, и вообще может интересна любая инфа ,может и ты захочешь вдвоем все же дешевле и легче.Заранее благодарен.Константин.

    ОтветитьУдалить
  6. На Гоа?... Я такую глупость написать не мог. Что там делать всю зиму? Сидеть на пляже и медитировать с такими же бомжеватыми идиотами у которых нет денег чтобы поехать в нормальное место?
    Не. Я стараюсь избегать мест скопления большого количества дураков. Уж куда-куда, а на Гоа я точно никогда не поеду :-)

    ОтветитьУдалить
  7. Извини, значит ошибся, я давно читал,значит не про гоа ты писал. Сорри)))

    ОтветитьУдалить
  8. Автору преогромная благодарность! Разобрался, поменял рынки на футбольные, как мне надо. Все ок. Только хочется решить еще пару задач:
    1. Как можно узнать, регулируемый рынок или нет?
    2. Для фишинга нерыночных кэфов хочу, чтобы бот мониторил появление новых рынков и сигналил об этом, скажем на почту. Думаю, что здесь не обойтись без использования БД. Что порекомендуешь, MySQL или что-то еще? Подозреваю, что ява умеет работать со многими СУБД, но еще не гуглил :)

    ОтветитьУдалить
  9. 1. Слово "регулируемый" не совсем понимаю. Если ты имеешь в виду "Managed", то узнать это из API можно только одним способом. Рынок ставок на победителя будет называться не "Match Odds", а "Match Odds Unmanaged"
    2. Java, действительно, умеет работать с любой базой данных. Однако, для такого пустяка создавать базу данных я бы не стал.

    ОтветитьУдалить
    Ответы
    1. 1. Спасибо, все просто оказывается. :)
      2. Я планировал такой алгоритм. Бот в цикле, скажем каждые пять минут, проверяет рынки и сравнивает, есть ли они уже в базе или нет. Если нет, то сигналит об этом и пишет в базу, чтобы в следующий прогон не сигналить их. Если БД не использовать, тогда что? В текстовый файлик писать и с ним сравнивать? Или у тебя другая мысль?

      Удалить
    2. Можно и базу. Но для работы с базами данных надо обладать кое-какими дополнительными знаниями. Без которых мало что у тебя получится.

      А можно так. Данные хранить в обычном массиве объектов, затем после добавления нового элемента, используя writeObject() пишешь в файл. Если программа упала и надо массив прочитать опять, используешь readObject() и получаешь свой массив как ни в чем ни бывало назад. Я думаю это проще чем работать с базами данных. Читай на тему "Сериализация в Ява" - http://habrahabr.ru/post/60317

      Удалить
    3. В качестве массива, разумеется, надо юзать класс ArrayList. В нем есть все что тебе надо, а самое главное метод поиска уже существующего элемента.

      Удалить
  10. FD, подскажи как заставить бот следить за двумя рынками на одном матче на протяжении, например, часа и проставляться тогда, когда кофы будут соответствовать заданным. Спасибо.

    ОтветитьУдалить
  11. Изучай теорию многопоточных приложений. Для современного программиста реализовывать многопоточность проще простого.

    ОтветитьУдалить
  12. Добрый день! когда ожидать пятую часть повествования?

    ОтветитьУдалить