Open Source: Пишем бота для биржи BTC-E

в 12:00, , рубрики: bitcoin, litecoin, objective-c, open source, биржа, заработок, криптовалюта, Финансы для всех, электронная коммерция, метки: , , , , , ,

Open Source: Пишем бота для биржи BTC E
Добрый день, дорогой %username%!
Совсем недавно я узнал про такую замечательную вещь, как биткоин. Не секрет, что последние 2-3 месяца наблюдался бурный рост этой криптовалюты. Казалось бы, ну есть у нас криптовалюта, а сбывать ее где? После моего предыдущего поста у меня появилось несколько лайткоинов. Естественно, я начал думать о способе сбыта легкой криптовалюты. Немного погуглив, я набрел на биржу BTC-E и, о великий котэ, увидел заветную кнопочку «Trade API».

С этого момента судьба следующих пары-тройки дней для меня была предопределена — я загорелся идеей воспользоваться этим самым API и сделать миллионы грязных долларов написать бота, который автоматически бы сбывал и покупал ресурсы. А так как я fully proficient (взято с моего профиля на elance.com) в Objective C, на нем я писать и буду.

Три дня прошло, и я все-таки осилил API. Зачем я выкладываю все это в открытый доступ? Что же, на это есть ряд причин:

  • Пора уже начинать делать что-то open source
  • Нужно популяризировать криптовалюту
  • Логики в моем «наброске» очень мало, так что алгоритмы куплипродажи заинтересованному придется писать самостоятельно
  • Мне банально нужен человек, финансист, который наглядно объяснит какими путями нужно идти. Возможно, даже согласится работать вместе

Внимание! Под катом разбор основных моментов работы с API; подводные камни, о которые мне пришлось споткнуться; исходники.

1. Разбор API

Заходим на сайт биржы в раздел FAQ, открываем вкладку «API». Можно покликать по Public Api, ответы будут приходить в JSON формате. Нравится мне этот формат. Уже хорошо, идем дальше.
Переходим по ссылке «Trade Api». Что мы видим? Нужно просто отправлять POST запросы и будет нам счастье! Отлично, а как это на Objective C-то реализовать? Вот незадача! До этого момента я ведь никогда и не думал о подобном на моем родном языке. Ничего страшного, разберемся.
Что у нас тут? Посылаем ключ API из профиля в заголовок «api»; POST запрос добавляем как тело запроса; а в «sign» передаем хеш нашего тела запроса, подписанный секретным ключом «secret». Круто. Где их взять? Заходим в профиль.

Open Source: Пишем бота для биржи BTC E

Ничего себе! И то, и то в одном месте! Вот это да, пока что я иду на правильном пути. Главное не забыть установить нужные права для наших ключей.
Давайте взглянем на тело стандартного ответа с публичного запроса из API. Попробуем узнать курс BTC/USD. Шлепнем по запросу. Получаем примерно следующее:

{"ticker":{"high":139,"low":126.6,"avg":132.8,"vol":971784.85609,"vol_cur":7345.76864,"last":136.396,"buy":137,"sell":136.396,"server_time":1365246479}}

Про JSON рассказывать не буду, в интернете по нему полно туториалов. Нас интересуют покупка и продажа. Напишем небольшой код для реализации запроса и его обработки! Вперед к коду!

Жми меня!

- (void)updateLtcToUsdRate {
    dispatch_async(kBgQueue, ^{
        NSData *data = [apiHandler getResponseFromPublicServerUrl:@"https://btc-e.com/api/2/ltc_usd/ticker"];
        
        NSError* error;
        NSDictionary* json = [NSJSONSerialization
                              JSONObjectWithData:data
                              options:kNilOptions
                              error:&error];
        
        NSDictionary* ticker = [json objectForKey:@"ticker"];
        NSString *sell = [ticker objectForKey:@"sell"];
        NSString *buy = [ticker objectForKey:@"buy"];
        ltcToUsdSellRateLabel.stringValue = addDollarSign(sell);
        ltcToUsdBuyRateLabel.stringValue = addDollarSign(buy);
    });
}

- (NSData *)getResponseFromPublicServerUrl:(NSString *)urlString {
    NSURL *url = [NSURL URLWithString:urlString];
    NSData *data = [NSData dataWithContentsOfURL:url];
    return data;
}

Пройдемся по коду. В проекте у нас пара классов, и BtceApiHandler работает с запросами на сервер. getResponseFromPublicServerUrl — это метод BtceApiHandler класса. Здесь никаких POST запросов отправлять не нужно, просто забираем данные с сервера. Дальше слегка обрабатываем JSON и получаем значения sell и buy. Покажем их на экран нашему пользователю (пока что только нам).
А что за addDollarSign? А вот тут я постиг приятные плюшки функций в Objective C. До сегодняшнего момента я их редко использовал, старался их избегать, но теперь легким движением руки появляются вот такие крендельки. Мелочь, а приятно.

NSString *addDollarSign(NSString *string) {
    NSString *newString = [NSString stringWithFormat:@"$%@",string];
    return newString;
}

Ну, с публичным API вроде как разобрались, если нет — Вам дорога к мануалу.
Что же происходит с личным API? Пока я разобрался с POST запросами в Objective C, я потерял приличное количество нервных клеток. Да и в самом API есть примеры алгоритмов и ввода, а что должно быть на выходе между Вами и сервером — не понятно. Пришлось спрашивать в чате самой биржи. Благо, приятные люди там есть, и мне помогли.
В чем проблема? Наверное, в том, что я никогда и не работал с POST запросами на своем родном языке. Только Java, только хардкор. Прыгнем в код класса BtceApiHandler.

- (void)setupInitialValues {
    api_key = @"P55XJOOF-207D9CCC-A7NDKIYK-KIMFCOXC-WJPJ21XY";
    secret_key = @"5bb4f0c55d6100d583505dc98eaff1df7d1ba281b5f8365686090d5cbbdb9846";
}

Нужно инициализировать две важные переменные. Позже можно будет сделать специальное окошко и так далее, но я просто захордкодил ключи. Не обольщайтесь, их я уже отключил.

Жми меня!

- (NSData *)getResponseFromServerForPost:(NSDictionary *)postDictionary {
    NSString *post;
    int i = 0;
    for (NSString *key in [postDictionary allKeys]) {
        NSString *value = [postDictionary objectForKey:key];
        if (i==0)
            post = [NSString stringWithFormat:@"%@=%@", key, value];
        else
            post = [NSString stringWithFormat:@"%@&%@=%@", post, key, value];
        i++;
    }
    post = [NSString stringWithFormat:@"%@&nonce=%@", post, getNonce()];
    
    NSString *signedPost = hmacForKeyAndData(secret_key, post);
    
    NSMutableURLRequest *request = [[NSMutableURLRequest alloc]
                                    initWithURL:
                                    [NSURL URLWithString:@"https://btc-e.com/tapi"]];
    [request setHTTPMethod:@"POST"];
    [request setValue:api_key forHTTPHeaderField:@"key"];
    [request setValue:signedPost forHTTPHeaderField:@"sign"];
    [request setHTTPBody:[post dataUsingEncoding: NSUTF8StringEncoding]];
    
    NSURLResponse *theResponse = NULL;
    NSError *theError = NULL;
    NSData *responseData = [NSURLConnection sendSynchronousRequest:request returningResponse:&theResponse error:&theError];
    return responseData;
}

А вот и монструозный метод отправки и обработки POST запроса! Мы передаем в него аргументом NSDictionary, в котором храним связки параметр-значение. Первая часть преобразует наш словарь в POST запрос, вторая часть забивает наш запрос стандартными данными (тип данных, url, key, sign), третья часть посылает запрос и возвращает NSData c ответом.

Жми меня!

NSString *hmacForKeyAndData(NSString *key, NSString *data) {
    const char *cKey = [key cStringUsingEncoding:NSASCIIStringEncoding];
    const char *cData = [data cStringUsingEncoding:NSASCIIStringEncoding];
    unsigned char cHMAC[CC_SHA512_DIGEST_LENGTH];
    CCHmac(kCCHmacAlgSHA512, cKey, strlen(cKey), cData, strlen(cData), cHMAC);
    NSMutableString *hashString = [NSMutableString stringWithCapacity:sizeof(cHMAC) * 2];
    for (int i = 0; i < sizeof(cHMAC); i++) {
        [hashString appendFormat:@"%02x", cHMAC[i]];
    }
    return hashString;
}

Особое внимание стоит уделить коду подписи данных в «sign». Почему? Да я на него убил больше суток! Больше суток хождения по интернетам и стаковерфловам. Происходит все просто: используя встроенные возможности языка, мы подписываем ключом данные и вытягиваем SHA512 хеш. Цикл, думаю, объяснять не стоит — там и так все очевидно. Раньше пытался получить значение через description наших данных — не получалось. Пришлось спросить на stackoverflow про этот небольшой циклик.

Жми меня!

NSString *getNonce() {
    NSTimeInterval timeStamp = [[NSDate date] timeIntervalSinceDate:[NSDate dateWithString:@"2012-04-18 00:00:01 +0600"]];
    int currentNonce = [NSNumber numberWithDouble: timeStamp].intValue;
    NSString *nonceString = [NSString stringWithFormat:@"%i",currentNonce];
    return nonceString;
}

А еще есть функция getNonce(). Параметр nonce передавать в POST запросе обязательно. Это инкремент, так что каждое последующее число должно быть больше предыдущего. Вот такие пироги. Ах да, эта функция считает время, прошедшее с моего совершеннолетия.

2. Что умеет делать наш бот?

Пока мы можем мониторить интересную информацию, смотреть за ростом и падением криптовалют. Ничего особенного, можно сказать, я просто обернул API в Objective C. Небольшой скриншот програмки-на-коленке:

Open Source: Пишем бота для биржи BTC E

3. Что делать дальше?

API побеждено, дело техники закончено. Впереди автоматизация процесса — а это уйма работы над алгоритмами. Сам я далеко не финансист, да что уж, даже не начинающий трейдер на рынке. Если вдруг кто из желающих здесь захочет со мной поработать, и возможно даже поднять немного денег — милости прошу ко мне в различные IM, контакты есть в моем хабрацентре.
А вдруг кто из Objective C программистов захочет позаниматься в свободное время этими механизмами и созданием со мной юзер-френдли UI? Опять же, милости прошу в IM.

Где код?

Исходники тут. Приятного просмотра. Все под лицензией GPL (первая попавшаяся мне open source лицензия). Качайте, компилируйте, пользуйтесь, распространяйте. Имя автора сохраняйте.

Если Вы нашли какую-то опечатку иили неточность — напишите, пожалуйста, мне в личку или в комментарии. Всем спасибо, а я пойду спать. Это API заставляло меня спать по 4-6 часов в сутки. Наконец-то высплюсь. Всем спасибо.

Автор: backmeupplz

Источник

Поделиться

  1. Юрий:

    Лалка:D зечем замазывать API ключи на первом фото, если ты их потом в коде показываешь:D api_key = @»P55XJOOF-207D9CCC-A7NDKIYK-KIMFCOXC-WJPJ21XY»;
    secret_key = @»5bb4f0c55d6100d583505dc98eaff1df7d1ba281b5f8365686090d5cbbdb9846″;

* - обязательные к заполнению поля