📜 ⬆️ ⬇️

dev || bet - the battle of programmers and technology



Dogs vs cats, drivers vs pedestrians, Coca-Cola vs Pepsi, BMW vs Mercedes, sausage vs cheese, uzvar vs juice, good vs evil, after all! But no, we, as always, argued about which programming language is better. Classical arguments about performance were used along with references to benchmarks that no one checked, syntax buns that you use once a year, popularity charts, lists of authoritative programmers using a particular language ... Then the conversation went smoothly into the discussion of the Battle of Oksimiron with someone there. Well, any conversation longer than 20 minutes comes down to discussing the price of Bitcoin.

In a strange way, the three themes merged into one, and the idea of ​​dev || bet was born.


The essence of the project


Two developers representing different technologies solve the same problem. The winner is the one who decides it in the shortest possible time, or gets a better result for a limited time.
')
Three commentators are watching the process on the participants' screens. They discuss their approach to solving, notice interesting moments, non-standard moves and just interesting pieces that any programmer has. On the other hand, they pay attention to unsuccessful decisions and failures.

We, the viewers, want to see how ordinary developers think, google, are wrong, and in the end, come to a decision. Moreover, it is more interesting for us to observe the plugging of leads over the library function than the click of the algorithms by the trained Olympiad.

For the pilot issue, we invited friends as participants, who identified the technologies used: Python vs JavaScript.

The task of the first release


In order for the project to be successful in 2018, its name must include a reference to cryptocurrency or blockchain. Therefore, a little dreaming, we came up with a simple task. The formal description that participants received was as follows:

Today, everyone is talking about cryptocurrency. There have been many cases when people sold bitcoins at an incredibly low price. Well, I once thought to buy 150 BTC for $ 15 ...
But what if we had a time machine that could send crypto commands to the broker in the past? Of course, we would have brought down the global financial system. Nevertheless, let's imagine that we have a car, or, at least, we are close to its creation. We want you, a dirty, hungry freelancer working for food, to create for us an algorithm that generates a sequence of commands for our broker client. (Which we ourselves created a year ago specifically for future use, of course.)

You can use Python or JavaScript. Please check your solution with the included test runner. The one whose decision will earn more money will be chosen to write a bot for a real client.

API specification v0.0.18


The task runner should be a pure function that takes two arguments:
prices => [{"btc": 1000}, ...] - Array of Bitcoin prices by day
initialSum => 100000 - Starting balance in USD
The function must return an array of commands. Commands are called in turn, one per day. That is, for 14 days you must have exactly 14 teams.
Example:
[{"op":"buy","amount":1},{"op":"sell","amount":1}]
Commands:
"buy" additional attributes: amount [float] - Buy BTC using current USD account
"sell" additional attributes: amount [float] - Sell BTC using current USD account
"pass" - skip day

The task description was intentionally made confusing and fuzzy to make the process more interesting. However, as a result, it played a cruel joke, and greatly increased the decision time of the participants.

Essentially, it was necessary to write a function that, according to the already known history of price changes, will issue instructions when it is worth buying bitcoins, when to sell, and when just to wait.

Implementation


The platform was chosen platform Codewars . Not that we chose for a long time, but it had everything we needed: support for more than 20 languages, a simple interface, the ability to add a draft of a task available by reference.

The codebattle.hexlet.io project, which has already been written on Habré, seems quite interesting. But the ability to see the opponent's code has seemed superfluous for us.

Since participants used Python and JavaScript, test runners were implemented on them:

Javascript
 'use strict'; const https = require('https'); const currencies = [ 'btc', 'eth', ]; const BASE_CURRENCY = 'usd'; const DEFAULT_CURRENCY = 'btc'; const fetchRates = (days, currency) => new Promise((res, rej) => { https.get(`https://min-api.cryptocompare.com/data/histoday?aggregate=1&e=CCCAGG&extraParams=CryptoCompare&limit=${days}&tryConversion=false&tsym=${BASE_CURRENCY.toUpperCase()}&fsym=${currency.toUpperCase()}`, (resp) => { let data = ''; resp.on('data', (chunk) => { data += chunk; }); resp.on('end', () => { data = JSON.parse(data); data = data.Data; res(data.map(datum => datum['close']).slice(0, -1)); }); }).on("error", err => rej(err)); }); const fetchAllRates = async (days, currencies) => { const prices = {}; for (let currency of currencies) { prices[currency] = await fetchRates(days, currency); } const len = prices[Object.keys(prices)[0]].length; const ret = []; for (let i = 0; i < len; i++) { let price = {}; for (let currency of currencies) { price[currency] = prices[currency][i]; } ret.push(price); } return ret; }; const checkStash = stash => { const vals = Object.values(stash); for (let val of vals) { Test.expect(val >= -Math.pow(10, -6), 'Invalid operation'); if (val < -Math.pow(10, -6)) { throw new Error(`Debts are not supported. Stash: ${JSON.stringify(stash)}`) } } }; const applyTask = (stash, task, prices) => { console.log('- performing task', stash, task, prices); const currency = task.currency || DEFAULT_CURRENCY; switch(task.op) { case 'buy': stash[currency] += task.amount; stash[BASE_CURRENCY] -= task.amount * prices[currency]; break; case 'sell': stash[currency] -= task.amount; stash[BASE_CURRENCY] += task.amount * prices[currency]; break; case 'pass': break; } return stash; }; const runner = async (trader, cases) => { for (let testCase of cases) { let prices = await fetchAllRates(testCase.days, currencies); let stash = testCase.amount; for (let currency of currencies) { stash[currency] = stash[currency] || 0; } console.log(`Testing amount ${stash[BASE_CURRENCY]}, days ${testCase.days}`); let tasks = await trader(prices, stash[BASE_CURRENCY]); for (let i in tasks) { if (!tasks.hasOwnProperty(i)) { continue; } let job = tasks[i]; let todo = job.length ? job : [job]; for (let row of todo) { await applyTask(stash, row, prices[i]); } checkStash(stash); } let result = Math.floor(stash[BASE_CURRENCY] * 100) / 100; console.log(`finished. Resulting amount: ${result}`); } }; runner(trader, [ { amount: { [BASE_CURRENCY]: 100, }, days: 100, }, ]); 


Python
 import urllib2 import json import math currencies = [ 'btc', 'eth', ] BASE_CURRENCY = 'usd' DEFAULT_CURRENCY = 'btc' def fetch_rates(days, currency): data = urllib2.urlopen( 'https://min-api.cryptocompare.com/data/histoday?aggregate=1&e=CCCAGG&extraParams=CryptoCompare&limit={}&tryConversion=false&tsym={}&fsym={}'.format( days, BASE_CURRENCY.upper(), currency.upper())).read() data = json.loads(data)['Data'] return [row['close'] for row in data][:-1] def fetch_all_rates(days, currencies): prices = {currency: fetch_rates(days, currency) for currency in currencies} return [{currency: prices[currency][i] for currency in currencies} for i in range(days)] def check_stash(stash): for currency in stash: test.assert_equals(stash[currency] >= -0.000001, True, 'Invalid operation') if stash[currency] < -0.000001: raise Exception('Debts are not supported. Stash: {}'.format(stash)) def apply_task(stash, task, prices): print '- performing task {} {} {}'.format(stash, task, prices) currency = task['currency'] if 'currency' in task else DEFAULT_CURRENCY if task['op'] == 'buy': stash[currency] += task['amount'] stash[BASE_CURRENCY] -= task['amount'] * prices[currency] elif task['op'] == 'sell': stash[currency] -= task['amount'] stash[BASE_CURRENCY] += task['amount'] * prices[currency] elif task['op'] == 'pass': pass return stash def runner(trader, cases): for testCase in cases: prices = fetch_all_rates(testCase['days'], currencies) stash = testCase['amount'] for currency in currencies: if currency not in stash: stash[currency] = 0 print 'Testing amount {}, days {}'.format(stash[BASE_CURRENCY], testCase['days']) tasks = trader(prices, stash[BASE_CURRENCY]) for i, job in enumerate(tasks): todo = job if isinstance(job, list) else [job] for row in todo: stash = apply_task(stash, row, prices[i]) check_stash(stash) result = math.floor(stash[BASE_CURRENCY] * 100) / 100 print 'finished. Resulting amount: {}'.format(result) runner(trader, [ { "amount": { BASE_CURRENCY: 100, }, "days": 100, } ]) 


The task itself is available on Codewars
And the decisions of the participants on Github

Thanks for attention! We are waiting for your comments, critics and opinions.

And what next? PHP vs. JS, .Net vs. Java, iOS vs. Android, React vs. Vue.js?

Source: https://habr.com/ru/post/351096/


All Articles