Blockchain elemzés Pythonnal

Most, hogy a kripto piacok erősen az egyet előre, egyet hátra elv szerint működnek (mindezt napi akár 10-15%-os elmozdulásokkal, amiket azért lehet rendesen nyerészkedni) és lényegében már mindenki enerváltan várakozik a augusztusi drámatagozatos óvodások előadására, ahol bal oldalon az UASF család mérhetetlen haragot táplál a jobb oldalon elhelyezkedő BitcoinABC család iránt, mely haragnak az eredeti okára már senki sem emlékszik, de a két család egy-egy sarja… oké, elkalandoztam.

Szóval a nagy rápihenésben végre van időm elővenni a kis házi barkács projektemet. Aki olvasta a self-info postomat, annak ismerős lehet az alábbi rész: “Amivel leginkább szeretnék foglalkozni, az ezen kettő témának (márhogy az Machine-Learning és a Cryptocurrency) összehozatala egy nagy projektté. Persze még messze a cél, de sikerül komolyabb eredményeket felmutatni egy olyan automatizált platform elkészítése kapcsán, ami segíthet hasznos predikciókat készíteni.

Ahol tartok:  az a blockchainek értelmezése és abból hasznos adatok kinyerése. Mivel mapság mindenki blockchain kóder akar lenni (már persze leszámítva azokat akik nem…), ezért gondoltam megosztok néhány nagyon egyszerű példát ami hasznos lehet azoknak akik tényleg csak most ismerkednek a témával. A továbbiakban két minta projektet mutatok be, az első egy egyszerű block explorer és statisztikáció, ami a legyűjti az utolsó 5 block (elmúlt egy óra) teljes tranzakció listáját és abból leválogatja azon bitcoin addresseket, amik az elmúlt egy órában a legtöbb bitcoint szerezték és azokat akik a legtöbbet vesztették el. A második projekt egy egyszerű address explorer, amivel le lehet kérdezni egy konkrét address teljes tranzakció történetét aggregálva blockonként.

Ezek a példák persze önmagukban kb semmit sem érnek, azonban beleillesztve ezeket egy keretbe nagyon is hasznosak lehetnek. Nézzük egy egyszerű példát:

  • Ismeretesek a nagyobb tőzsdék (kraken, bitfinex, pol, stb.) wallet addressei. Ha az elmúlt időszakban ezen addressek irányába nagy mennyiségű bitcoin áramlik olyan privát addressekről amik jelentősebb tranzakciós múlttal rendelkeznek, akkor jogosan lehet feltételezni, hogy ezeket azért rakják oda be, mert el akarják adni vagy fiatra/más kriptora váltani. Ez ugye azt jelentheti, hogy csökkenni fog az adott crypto pénz piaci kapitalizációja. Nyilván ez vice-versa is igaz.

Ennél több részletet egyelőre nem osztanék meg a projektről, de megígérem, hogy folyamatosan közölni fogom a részleteket itt a blogon, ahogy haladok előre. Előbb utóbb tisztulni fog a kép 🙂

Ahogy a címben is szerepel pythonnal kezdtem neki a témának és egyébként blockchain.info API-ját használom az elemzéshez. Ennek fő oka, hogy nincs kedvem letölteni egy teljes nodeot csak az elemezéshez. Persze hamarosan erre is rá fogok kényszerűlni a sebesség miatt, de egyelőre jól elvagyok a remote API-val.

Nézzük mindjárt is az első példát: utolsó 5 block letöltése és abból top10 gainer/looser aggregáció:

#!/usr/bin/python3
import datetime, time, json, sys, socket, http.client
import urllib.request, urllib.parse, urllib.error

_addrs = {};

def manage_addr (addr, inp, out):
_found = 0
for i in _addrs.keys():
if (i == addr): _found = 1
if (_found == 0):
_addrs[addr] = [0-inp+out,inp,out];
else:
_addrs[addr][0] = _addrs[addr][0] - inp + out;
_addrs[addr][1] = _addrs[addr][1] + inp;
_addrs[addr][2] = _addrs[addr][2] + out;

#--------- Get the latest block for hash ID
conn = http.client.HTTPSConnection("blockchain.info");
conn.request('GET',urllib.parse.quote('/latestblock'))
response = conn.getresponse()

print (response.status, response.reason)
res = json.loads(response.read().decode());
response2 = "";
res2 = {};

_loop = 5;

while (_loop):
#--------- First time get the _hash from /latestblock
if (_loop == 5): _hash = res['hash'];
#--------- Later get the _hash from previously handled block's prev_block field
else: _hash = res2['prev_block'];

_loop=_loop-1;
#--------- Get the raw block over API to examine transactions
conn.request('GET', urllib.parse.quote("/rawblock/"+_hash));
response2 = conn.getresponse();
res2 = json.loads(response2.read().decode());
print(_hash)
for k in res2['tx']:
#--------- Input parts of a uniq transcation (sender addresses)
for j in k['inputs']:
if ('prev_out' in j and 'addr' in j['prev_out'] and 'value' in j['prev_out']):
manage_addr(j['prev_out']['addr'],j['prev_out']['value'],0);
#--------- Output parts of a uniq transcation (beneficiary addresses)
for j in k['out']:
if ('addr' in j and 'value' in j):
manage_addr(j['addr'], 0, j['value']);

#--------- Convert addrs dictionary to array for ordering...
_Xaddrs = [];
for k in _addrs.keys():
_Xaddrs.append([k,_addrs[k][0],_addrs[k][1],_addrs[k][2]]);

_Saddrs = sorted(_Xaddrs, key=lambda x: -x[1])
print ('top 10 gainer:')
for k in range(0,10):
print (_Saddrs[k]);

print ('top 10 looser:')
for k in range(len(_Xaddrs)-10, len(_Xaddrs)):
print (_Saddrs[k]);

Nézzük mit dob eredményül a kód:


[ blockexplorer]$ ./test2.py

200 OK
0000000000000000015d62bd4c760955f52a58c81cd05e6d5f13a37ce629dee7
0000000000000000015b70afff57348c99f598974953d0457cec475fdb21609a
0000000000000000017cef1583464c6436329467cc1fa532792b8d942041ee8f
000000000000000000d729aef41b7ef699e0e60aa036dc85416b7a518a79839d
0000000000000000014a2873d28cffaaabf0eebd5e0a63f297e0f6ce8f57c884

top 10 gainer:
['1NYAd6fA2dc5xowuweFUSDRqRTEzDwk28', 149999960000, 0, 149999960000
['3CtwSYfh6XZ7jSyiCuFSBJKZZ2pxp86scN', 149336883009, 0, 149336883009
['3M1sUygMD5kM9RxUwVVqbrWTS449hcD4JP', 146570420722, 0, 146570420722]
['35sDBj73XnWNcG1cgmHw5R9SxFc61D5Dtb', 99999906773, 0, 99999906773]
['1ChCM6s2Fv7J81Km3JLNbnxSPAhbJZ8pM6', 81638520368, 0, 81638520368]
['18pqBWvW5sEnCFp3cmxvP5XAM6qynxT2VL', 75671601279, 0, 75671601279]
['15fwb75zcQzNrWTont7bn7dU4twt7psX7Q', 52832982092, 0, 52832982092]
['1E1Ppc4nsdKjss3a7hwcsqtR4XjdcHqUEw', 52225636162, 0, 52225636162
['198cWvvG5ynt3BH1EXuyeSxEV6RaumPaaA', 50000000000, 0, 50000000000]
['3D31mujtmX24qCZvrfnsSqXx4jR3gFL7xh', 50000000000, 0, 50000000000]

top 10 looser:
['16SfWnL3VhxovKvQeCsJjd2b98Uvf1GYwx', -38119750181, 38119750181, 0]
['1FKzSdcBYttRJRVrskVwx6xaHXSQhgAiuV', -40960073668, 40960073668, 0]
['12cgpFdJViXbwHbhrA3TuW1EGnL25Zqc3P', -44853736833, 70880504330, 26026767497]
['17A16QmavnUfCW11DAApiJxp7ARnxN5pGX', -45859513097, 1259714142285, 1213854629188]
['1Dvz5jQomoUd5JyU87QddgX1w9BHCa7BQm', -54000000000, 54000000000, 0]
['19YDo3MUhftZeFUdqmARLgxZrSqcsVdcP6', -75843283338, 75843283338, 0]
['13QDoWWAPZMFQ5Z2ADKEtTNpNKcmx7EBYt', -95452028128, 95452028128, 0]
['3BgnS32gxqaHw6JRkGzrvCEN5w77uJoTng', -149481271129, 149481271129, 0]
['3HTgy4nBUJtPNaEufjWRcQyQWFiuXgwsSV', -150000000000, 150000000000, 0]
['3NGe3phrYHiuLZ3WMBcq7pHLCWqAWAonYr', -150078669775, 150078669775, 0]

[ blockexplorer]$

Az összes bitcoin ‘satoshi’ mértékegységben van megadva, tehát amikor azt látod pl, hogy egy bitcoin address-ről lepakoltak -150078669775 satoshit, az azt jelenti, hogy -1500,78669775 BTC tűnt el a konkrét számláról.

Hogy mi az értelme ezekek a számoknak, mit tudhatunk meg ebből?  Nagyon messzemenő következtetéseket nem szeretnék levonni, de az jól látszik, hogy két olyan address szerepel, ahol az elmúlt egy órában kétirányú forgalom is történt, ezek: 17A16QmavnUfCW11DAApiJxp7ARnxN5pGX és 17A16QmavnUfCW11DAApiJxp7ARnxN5pGX. Mindkettő a Poloniex tőzsdéhez tartozik. Az első a fő btc walletjük, a második pedig egy temporáris wallet. Tehát máris az látszik, hogy az elmúlt egy órában jelentősebb kivonás történt a poloniexről, azaz a tőzsdézők “hazahozták” a megtermelt vagyonukat.

Ennek a részletesebb megértéséhez nézzük mit tudhatunk meg a második minta kódból:

#!/usr/bin/python3
import datetime, time, json, sys, socket, http.client
import urllib.request, urllib.parse, urllib.error

def get_trans(tx, addr_id, addr):
_balance = tx['final_balance'] / 100000000;
cur.execute ('UPDATE ???? set balance=%.08f where id=%d' % (_balance, addr_id));
for j in tx['txs']:
_ret = 0;
for i in j['inputs']:
if ('prev_out' in i and 'addr' in i['prev_out'] and i['prev_out']['addr'] == addr): _ret = _ret - int(i['prev_out']['value'])/100000000 ;
for i in j['out']:
if ('addr' in i and i['addr'] == addr): _ret = _ret + int(i['value'])/100000000 ;
_balance = _balance - _ret;
if ('block_height' in j): cur.execute ("INSERT INTO ???? VALUES(0, %d, %d, FROM_UNIXTIME(%d), %.08f, %.08f)" % (addr_id, j['block_height'],j['time'],_ret,_balance));
con.commit();

try:
con = mdb.connect('', '', '', '')
con.autocommit(False);
cur = con.cursor()

cur.execute('SELECT id,addr from ????')
a = cur.fetchall();
conn = http.client.HTTPSConnection("blockchain.info");
for i in a:

conn.request('GET',urllib.parse.quote('/rawaddr/'+i[1]))
response = conn.getresponse()

print (i[1], response.status, response.reason)
res = json.loads(response.read().decode());

get_trans(res,i[0],i[1]);

finally:
if 'cur' in globals(): cur.close();
if 'con' in globals(): con.close();

A mintaprogram egy adatbázisban tárolt address lista minden elemére letölti az ahhoz tartozó historikus tranzakciókat. Az így kapott tranzakció lista már könnyedén vizualizálható. Nézzük mi történik éppen most a Poloniex fő walletjében:

Érdekességképpen ide rakom a Poloniex-en a BTCUSD árfolyamot is ugyanezen időszakban:

Látható, hogy júli 9 és júli 11 között nagy mennyiségű új bitcoin áramlott be a Poloniexre, amit júli 12-én eladtak vagy kiutaltak onnan, a lényeg, hogy ezzel párhuzamosan elkezdett emelkedni az árfolyam. Majd ma kora délután óta újabb nagy mennyiségű btc érkezett a Poloniex számlájára, amit követően újra elkezdett bezuhanni az árfolyam. A BTC árfolyam olyankor zuhan amikor hírtelen sok BTC érkezik a Polo számlájára és akkor emelkedik, amikor onnan kifelé csorog a pénz.

FONTOS: A fentebbi okfejtés csak egy példa, egy lehetséges olvasata az ábrának és messze nem megalapozott elemzés. A bitcoin blockláncban szereplő 14 millió wallet nagy része gyakorlatilag csak azért létezik, hogy a tőzsdék és a nagyhalak elfedjék a valós pénzmozgásaikat (lásd shapeshifting és hasonló szolgáltatások). Nem gondolom, hogy egy-egy address megfigyelésével bármilyen értelmes predikciót is lehetne tenni, ez a ‘vonat’ még évekkel ezelőtt elment… Ettől függetlenül a korreláció vizsgálat és a regresszió mint eszköz még rejthet meglepetéseket 🙂 (legalábbis nagyon remélem…)

Bookmark the permalink.

2 Comments

  1. > de az jól látszik, hogy két olyan address szerepel, ahol az elmúlt egy órában kétirányú forgalom is történt, ezek: 17A16QmavnUfCW11DAApiJxp7ARnxN5pGX és 17A16QmavnUfCW11DAApiJxp7ARnxN5pGX.

    Mindket address ugyanaz 🙂

    • Jogos, sikerült rosszul másolnom, a helyes cím: 12cgpFdJViXbwHbhrA3TuW1EGnL25Zqc3P

      Javítom a postban is, köszi a jelzést. Jó látni, hogy valaki el is olvassa az ilyen jellegű postjaimat is 🙂

Szólj hozzá: