├── .gitigore ├── pics ├── 1.png ├── 2.png ├── 3.png ├── 4.png ├── 5.png └── logo.png ├── __pycache__ └── encryption_helper.cpython-36.pyc ├── data ├── __pycache__ │ ├── html_helper.cpython-35.pyc │ ├── html_helper.cpython-36.pyc │ ├── whale_data.cpython-35.pyc │ ├── whale_data.cpython-36.pyc │ ├── bfs_on_account.cpython-36.pyc │ ├── whale_eth_tx_data.cpython-35.pyc │ ├── whale_eth_tx_data.cpython-36.pyc │ ├── whale_operation.cpython-35.pyc │ ├── whale_operation.cpython-36.pyc │ ├── whale_token_tx_data.cpython-35.pyc │ └── whale_token_tx_data.cpython-36.pyc ├── whale_data.py ├── whale_token_tx_data.py ├── whale_eth_tx_data.py ├── html_helper.py └── whale_operation.py ├── plot ├── __pycache__ │ ├── plotly_helper.cpython-35.pyc │ └── plotly_helper.cpython-36.pyc └── plotly_helper.py ├── analysis ├── __pycache__ │ ├── binance_draw.cpython-36.pyc │ ├── bittrex_draw.cpython-36.pyc │ ├── poloniex_draw.cpython-36.pyc │ ├── bussiness_logic.cpython-35.pyc │ ├── bussiness_logic.cpython-36.pyc │ ├── holder_tracking.cpython-35.pyc │ ├── holder_tracking.cpython-36.pyc │ ├── coinmarketcap_draw.cpython-35.pyc │ ├── coinmarketcap_draw.cpython-36.pyc │ ├── calculate_historical.cpython-35.pyc │ ├── calculate_historical.cpython-36.pyc │ ├── calculate_holding_amount.cpython-35.pyc │ └── calculate_holding_amount.cpython-36.pyc ├── holder_tracking.py ├── coinmarketcap_draw.py ├── calculate_holding_amount.py ├── calculate_historical.py └── bussiness_logic.py ├── requirement.txt ├── LICENSE ├── README.md └── main.py /.gitigore: -------------------------------------------------------------------------------- 1 | __pycache__/ 2 | *.pyc 3 | */*.pyc 4 | -------------------------------------------------------------------------------- /pics/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DeuroIO/erc20-ico-onchain-technical-analysis/HEAD/pics/1.png -------------------------------------------------------------------------------- /pics/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DeuroIO/erc20-ico-onchain-technical-analysis/HEAD/pics/2.png -------------------------------------------------------------------------------- /pics/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DeuroIO/erc20-ico-onchain-technical-analysis/HEAD/pics/3.png -------------------------------------------------------------------------------- /pics/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DeuroIO/erc20-ico-onchain-technical-analysis/HEAD/pics/4.png -------------------------------------------------------------------------------- /pics/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DeuroIO/erc20-ico-onchain-technical-analysis/HEAD/pics/5.png -------------------------------------------------------------------------------- /pics/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DeuroIO/erc20-ico-onchain-technical-analysis/HEAD/pics/logo.png -------------------------------------------------------------------------------- /__pycache__/encryption_helper.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DeuroIO/erc20-ico-onchain-technical-analysis/HEAD/__pycache__/encryption_helper.cpython-36.pyc -------------------------------------------------------------------------------- /data/__pycache__/html_helper.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DeuroIO/erc20-ico-onchain-technical-analysis/HEAD/data/__pycache__/html_helper.cpython-35.pyc -------------------------------------------------------------------------------- /data/__pycache__/html_helper.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DeuroIO/erc20-ico-onchain-technical-analysis/HEAD/data/__pycache__/html_helper.cpython-36.pyc -------------------------------------------------------------------------------- /data/__pycache__/whale_data.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DeuroIO/erc20-ico-onchain-technical-analysis/HEAD/data/__pycache__/whale_data.cpython-35.pyc -------------------------------------------------------------------------------- /data/__pycache__/whale_data.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DeuroIO/erc20-ico-onchain-technical-analysis/HEAD/data/__pycache__/whale_data.cpython-36.pyc -------------------------------------------------------------------------------- /data/__pycache__/bfs_on_account.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DeuroIO/erc20-ico-onchain-technical-analysis/HEAD/data/__pycache__/bfs_on_account.cpython-36.pyc -------------------------------------------------------------------------------- /plot/__pycache__/plotly_helper.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DeuroIO/erc20-ico-onchain-technical-analysis/HEAD/plot/__pycache__/plotly_helper.cpython-35.pyc -------------------------------------------------------------------------------- /plot/__pycache__/plotly_helper.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DeuroIO/erc20-ico-onchain-technical-analysis/HEAD/plot/__pycache__/plotly_helper.cpython-36.pyc -------------------------------------------------------------------------------- /analysis/__pycache__/binance_draw.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DeuroIO/erc20-ico-onchain-technical-analysis/HEAD/analysis/__pycache__/binance_draw.cpython-36.pyc -------------------------------------------------------------------------------- /analysis/__pycache__/bittrex_draw.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DeuroIO/erc20-ico-onchain-technical-analysis/HEAD/analysis/__pycache__/bittrex_draw.cpython-36.pyc -------------------------------------------------------------------------------- /analysis/__pycache__/poloniex_draw.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DeuroIO/erc20-ico-onchain-technical-analysis/HEAD/analysis/__pycache__/poloniex_draw.cpython-36.pyc -------------------------------------------------------------------------------- /data/__pycache__/whale_eth_tx_data.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DeuroIO/erc20-ico-onchain-technical-analysis/HEAD/data/__pycache__/whale_eth_tx_data.cpython-35.pyc -------------------------------------------------------------------------------- /data/__pycache__/whale_eth_tx_data.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DeuroIO/erc20-ico-onchain-technical-analysis/HEAD/data/__pycache__/whale_eth_tx_data.cpython-36.pyc -------------------------------------------------------------------------------- /data/__pycache__/whale_operation.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DeuroIO/erc20-ico-onchain-technical-analysis/HEAD/data/__pycache__/whale_operation.cpython-35.pyc -------------------------------------------------------------------------------- /data/__pycache__/whale_operation.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DeuroIO/erc20-ico-onchain-technical-analysis/HEAD/data/__pycache__/whale_operation.cpython-36.pyc -------------------------------------------------------------------------------- /analysis/__pycache__/bussiness_logic.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DeuroIO/erc20-ico-onchain-technical-analysis/HEAD/analysis/__pycache__/bussiness_logic.cpython-35.pyc -------------------------------------------------------------------------------- /analysis/__pycache__/bussiness_logic.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DeuroIO/erc20-ico-onchain-technical-analysis/HEAD/analysis/__pycache__/bussiness_logic.cpython-36.pyc -------------------------------------------------------------------------------- /analysis/__pycache__/holder_tracking.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DeuroIO/erc20-ico-onchain-technical-analysis/HEAD/analysis/__pycache__/holder_tracking.cpython-35.pyc -------------------------------------------------------------------------------- /analysis/__pycache__/holder_tracking.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DeuroIO/erc20-ico-onchain-technical-analysis/HEAD/analysis/__pycache__/holder_tracking.cpython-36.pyc -------------------------------------------------------------------------------- /data/__pycache__/whale_token_tx_data.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DeuroIO/erc20-ico-onchain-technical-analysis/HEAD/data/__pycache__/whale_token_tx_data.cpython-35.pyc -------------------------------------------------------------------------------- /data/__pycache__/whale_token_tx_data.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DeuroIO/erc20-ico-onchain-technical-analysis/HEAD/data/__pycache__/whale_token_tx_data.cpython-36.pyc -------------------------------------------------------------------------------- /analysis/__pycache__/coinmarketcap_draw.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DeuroIO/erc20-ico-onchain-technical-analysis/HEAD/analysis/__pycache__/coinmarketcap_draw.cpython-35.pyc -------------------------------------------------------------------------------- /analysis/__pycache__/coinmarketcap_draw.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DeuroIO/erc20-ico-onchain-technical-analysis/HEAD/analysis/__pycache__/coinmarketcap_draw.cpython-36.pyc -------------------------------------------------------------------------------- /analysis/__pycache__/calculate_historical.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DeuroIO/erc20-ico-onchain-technical-analysis/HEAD/analysis/__pycache__/calculate_historical.cpython-35.pyc -------------------------------------------------------------------------------- /analysis/__pycache__/calculate_historical.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DeuroIO/erc20-ico-onchain-technical-analysis/HEAD/analysis/__pycache__/calculate_historical.cpython-36.pyc -------------------------------------------------------------------------------- /analysis/__pycache__/calculate_holding_amount.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DeuroIO/erc20-ico-onchain-technical-analysis/HEAD/analysis/__pycache__/calculate_holding_amount.cpython-35.pyc -------------------------------------------------------------------------------- /analysis/__pycache__/calculate_holding_amount.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DeuroIO/erc20-ico-onchain-technical-analysis/HEAD/analysis/__pycache__/calculate_holding_amount.cpython-36.pyc -------------------------------------------------------------------------------- /analysis/holder_tracking.py: -------------------------------------------------------------------------------- 1 | # calculate holder number over time 2 | # input: acc_holding_values_dict 3 | # key: timestamp 4 | # value: dict 5 | # key: account 6 | # value: amount 7 | MINIMUM_TOKEN_AMOUNT_REQUIRED = 0.0 8 | 9 | def track_holder_number_over_time(acc_holding_values_dict): 10 | holder_number_over_time = [] 11 | for timestamp in acc_holding_values_dict: 12 | number = 1 13 | t_dict = acc_holding_values_dict[timestamp] 14 | print(timestamp) 15 | for acc in t_dict: 16 | if t_dict[acc] > MINIMUM_TOKEN_AMOUNT_REQUIRED: 17 | number += 1 18 | print("\t{}:{}".format(acc,t_dict[acc])) 19 | holder_number_over_time.append(number) 20 | return holder_number_over_time 21 | -------------------------------------------------------------------------------- /requirement.txt: -------------------------------------------------------------------------------- 1 | beautifulsoup4==4.6.0 2 | blinker==1.3 3 | bs4==0.0.1 4 | cfscrape==1.9.1 5 | chardet==2.3.0 6 | cloud-init==17.1 7 | command-not-found==0.3 8 | configobj==5.0.6 9 | cryptography==1.2.3 10 | decorator==4.2.1 11 | idna==2.0 12 | ipython-genutils==0.2.0 13 | Jinja2==2.8 14 | joblib==0.11 15 | jsonpatch==1.10 16 | jsonpointer==1.9 17 | jsonschema==2.6.0 18 | jupyter-core==4.4.0 19 | language-selector==0.1 20 | lxml==4.1.1 21 | MarkupSafe==0.23 22 | nbformat==4.4.0 23 | numpy==1.14.0 24 | oauthlib==1.0.3 25 | pandas==0.22.0 26 | plotly==2.2.3 27 | prettytable==0.7.2 28 | pyasn1==0.1.9 29 | pycurl==7.43.0 30 | PyExecJS==1.5.0 31 | pygobject==3.20.0 32 | PyJWT==1.3.0 33 | pyserial==3.0.1 34 | python-apt==1.1.0b1 35 | python-dateutil==2.6.1 36 | python-debian==0.1.27 37 | python-systemd==231 38 | pytz==2017.3 39 | PyYAML==3.11 40 | requests==2.9.1 41 | six==1.10.0 42 | ssh-import-id==5.5 43 | traitlets==4.3.2 44 | ufw==0.35 45 | unattended-upgrades==0.1 46 | urllib3==1.13.1 47 | WALinuxAgent==2.2.17 48 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Deuro 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /plot/plotly_helper.py: -------------------------------------------------------------------------------- 1 | import plotly.plotly as py 2 | from plotly.graph_objs import * 3 | 4 | py.sign_in('gelei', 'SYLSznL3X1AeEV724w1q') 5 | 6 | def plot_helper(fig,filename,trails=0,max_trail=5): 7 | if trails >= max_trail: return "" 8 | try: 9 | plot_url = py.plot(fig,filename=filename) 10 | return plot_url 11 | except Exception as e: 12 | print(e) 13 | print("failed to plot.retry...") 14 | return plot_helper(fig,filename,trails=trails+1) 15 | 16 | def plot_using_plotly(title,traces,filename): 17 | data = Data(traces) 18 | layout = { 19 | "autosize": True, 20 | "height": 1000, 21 | "showlegend": True, 22 | "title": title, 23 | "width": 2000, 24 | "xaxis": { 25 | "autorange": True, 26 | "title": "Time (days)", 27 | }, 28 | "yaxis": { 29 | "autorange": True, 30 | "title": "Token Amount" 31 | }, 32 | "yaxis2":{ 33 | "title":'Price(USD)', 34 | "overlaying":'y', 35 | "side":'right' 36 | } 37 | } 38 | fig = Figure(data=data, layout=layout) 39 | plot_url = plot_helper(fig,filename) 40 | 41 | print(title + " --- " + plot_url) 42 | return plot_url 43 | # plot_using_plotly("RDN Top Investor",[trace1,trace2,trace3]) 44 | -------------------------------------------------------------------------------- /analysis/coinmarketcap_draw.py: -------------------------------------------------------------------------------- 1 | import requests 2 | import pandas as pd 3 | from dateutil.parser import parse 4 | import numpy as np 5 | import datetime 6 | 7 | def coinmarketcap_data(symbol): 8 | url = 'https://graphs2.coinmarketcap.com/currencies/{}/'.format(symbol) 9 | data = requests.get(url).json() 10 | 11 | market_cap_by_available_supply = data['market_cap_by_available_supply'] 12 | df = pd.DataFrame(np.array(market_cap_by_available_supply), columns = ['time','market_cap']) 13 | df['time'] = pd.to_datetime(df['time'], unit='ms') 14 | df['market_cap'] = df['market_cap'].astype('float64') 15 | df.set_index('time', inplace=True) 16 | 17 | price_btc = data['price_btc'] 18 | price_btc = [i[1] for i in price_btc] 19 | se = pd.Series(price_btc) 20 | df['price_btc'] = se.values 21 | df['price_btc'] = df['price_btc'].astype('float64') 22 | 23 | price_usd = data['price_usd'] 24 | price_usd = [i[1] for i in price_usd] 25 | se = pd.Series(price_usd) 26 | df['price_usd'] = se.values 27 | df['price_usd'] = df['price_usd'].astype('float64') 28 | 29 | volume_usd = data['volume_usd'] 30 | volume_usd = [i[1] for i in volume_usd] 31 | se = pd.Series(volume_usd) 32 | df['volume_usd'] = se.values 33 | df['volume_usd'] = df['volume_usd'].astype('float64') 34 | df['volume_token'] = df['volume_usd'] / df['price_usd'] 35 | 36 | times = df.index.values 37 | t_arr = [] 38 | for time in times: 39 | t = datetime.datetime.utcfromtimestamp(time.tolist()/1e9) 40 | t_arr.append(t) 41 | return (df,t_arr) 42 | 43 | if __name__ == "__main__": 44 | df,times = coinmarketcap_data('raiden-network-token') 45 | print(df) 46 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ICO Top holder analysis 2 | > *"Human tuned heuristic Python Library for ICO company analysis."* 3 | 4 | ![](pics/logo.png) 5 | 6 | ### Table of Contents 7 | * [Installation](#installation) 8 | * [Usage](#usage) 9 | * [Customization](#customization) 10 | * [License](#license) 11 | 12 | ### Installation 13 | Clone the git repository: 14 | ```console 15 | $ git clone https://github.com/DeuroIO/erc20-ico-onchain-technical-analysis.git && cd erc20-ico-onchain-technical-analysis 16 | ``` 17 | 18 | Install necessary dependencies 19 | ```console 20 | $ pip install -r requirement.txt 21 | ``` 22 | 23 | ![img](http://www.dhanashriacademy.com/market/wp-content/uploads/2017/10/WHAT-IS-TECHNICAL-ANALYSIS.jpg) 24 | 25 | ### Usage 26 | Run the main business logic 27 | ```python 28 | python main.py 29 | ``` 30 | #### 1.total-abt-exchange-analysis 31 | https://plot.ly/~gelei/586/total-abt-exchange-analysis-bittrex-bitfinex-binance-poloniexliquiio-etherdelta-/#/ 32 | ![img](pics/1.png) 33 | 34 | #### 2.top-50-list-and-their-token-amount-without-counting-the-exchange 35 | https://plot.ly/~gelei/588/top-50-list-and-their-token-amount-without-counting-the-exchange/#/ 36 | ![img](pics/2.png) 37 | 38 | #### 3.top-50-token-amount-moving-average-without-counting-the-exchange 39 | https://plot.ly/~gelei/590/top-50-token-amount-moving-average-without-counting-the-exchange/#/ 40 | ![img](pics/3.png) 41 | 42 | #### 4.exchange-token-amount 43 | https://plot.ly/~gelei/592/exchange-token-amount/#/ 44 | ![img](pics/4.png) 45 | 46 | #### 5.hourly-{token}-exchange-analysis 47 | https://plot.ly/~gelei/594/hourly-abt-exchange-analysis-bittrex-bitfinex-binance-poloniexliquiio-etherdelta/#/ 48 | ![img](pics/5.png) 49 | 50 | ### Customization 51 | If you want to customize for a different token, you can change the address in the code for now. In the near future, we will support cli integration. 52 | 53 | 54 | ### License 55 | This code has been released under the [MIT License](LICENSE). 56 | -------------------------------------------------------------------------------- /data/whale_data.py: -------------------------------------------------------------------------------- 1 | from .whale_operation import find_whale_txs 2 | from .html_helper import find_tx_given_token_contract_address 3 | import datetime 4 | import time 5 | 6 | Watch_addr = "0x255aa6df07540cb5d3d297f0d0d4d84cb52bc8e6" #Radien Network 7 | 8 | #Whale operation: etherdelta,binance,liqui.io 9 | exchnage_accounts = ['0x8d12a197cb00d4747a1fe03395095ce2a5cc6819','0x3f5ce5fbfe3e9af3971dd833d26ba9b5c936f0be'] 10 | 11 | def find_interstering_accounts(start=1,end=1): 12 | # Find accounts 13 | txs,total_number_of_pages = find_tx_given_token_contract_address(Watch_addr,start,end) 14 | return txs,total_number_of_pages 15 | 16 | def find_whale_account_token_tx(escape_accounts,start,end): 17 | all_arr,_ = find_tx_given_token_contract_address(Watch_addr,start,end) 18 | print("-------------------find_whale_account_token_tx--------------------") 19 | print("len(all_arr): {}".format(len(all_arr))) 20 | counter = 0 21 | acc_features = dict() 22 | for account in all_arr: 23 | counter += 1 24 | if account in escape_accounts: 25 | continue 26 | else: 27 | print("{}/{} {}".format(counter,len(all_arr),account)) 28 | num_txs = find_whale_txs(Watch_addr,account) 29 | wait_time = 1 30 | while len(num_txs) == 0: 31 | print("wait_time: {}".format(wait_time)) 32 | time.sleep(wait_time) 33 | wait_time *= 2 34 | print("\tnumber of txs:{}".format(num_txs)) 35 | num_txs = find_whale_txs(Watch_addr,account) 36 | acc_features[account] = num_txs 37 | time.sleep(1) 38 | return acc_features 39 | 40 | def find_exchange_txs(): 41 | all_arr = exchnage_accounts 42 | print("-------------------find_exchange_txs--------------------") 43 | print("len(all_arr): {}".format(len(all_arr))) 44 | counter = 0 45 | acc_features = dict() 46 | for account in all_arr: 47 | counter += 1 48 | print("{}/{} {}".format(counter,len(all_arr),account)) 49 | acc_features[account] = find_whale_txs(Watch_addr,account) 50 | time.sleep(1) 51 | return acc_features 52 | -------------------------------------------------------------------------------- /data/whale_token_tx_data.py: -------------------------------------------------------------------------------- 1 | from .whale_eth_tx_data import * 2 | from .html_helper import get_html_by_url 3 | import sys 4 | 5 | def identify_investor_type_helper_token_helper(soup): 6 | no_matching_exist = soup.find("font",{"color":"black"}) 7 | if no_matching_exist is not None: 8 | return None 9 | trs = soup.find("div",{"class":"table-responsive"}).findAll("tr") 10 | tx_arr = [] 11 | for tr_index,tr in enumerate(trs): 12 | if tr_index != 0: 13 | tds = tr.findAll("td") 14 | for td_index,td in enumerate(tds): 15 | if td_index == 3: 16 | # type 17 | tx_type = td.find("span").text 18 | if "IN" in tx_type: 19 | tx_type = "IN" 20 | else: 21 | tx_type = 'OUT' 22 | elif td_index == 4: 23 | #to 24 | m_a = td.find("a") 25 | if m_a is None: 26 | m_a = td.find("span") 27 | to_address = m_a.text 28 | to_address = to_address.lower() 29 | tx_arr.append([tx_type,to_address]) 30 | for tx in tx_arr: 31 | [tx_type,to_address] = tx 32 | if tx_type == "OUT" and (("auction" in to_address) or ("sale" in to_address) or ("etherdelta" in to_address)): 33 | return personal_type 34 | if tx_type == "OUT" and (("binance" in to_address) or ("liqui" in to_address) or ("bittrex" in to_address) or ("poloniex" in to_address) or ("bitfinex" in to_address)): 35 | return exchange_type 36 | return affliate_type 37 | 38 | def identify_investor_type_token(account): 39 | url = "https://etherscan.io/tokentxns?a={}".format(account) 40 | soup = get_html_by_url(url) 41 | total_number_of_page = get_total_number_of_page(soup) 42 | print("\t\t[identify_investor_type] Token:{} for {}".format(total_number_of_page,account)) 43 | 44 | investor_type = identify_investor_type_helper_token_helper(soup) 45 | if investor_type != affliate_type: return investor_type 46 | 47 | for i in range(2,min(bfs_max_depth+1,total_number_of_page+1)): 48 | print("\t\t{}".format(i)) 49 | soup = get_html_by_url("{}&p={}".format(url,i)) 50 | investor_type = identify_investor_type_helper_token_helper(soup) 51 | if investor_type != affliate_type: return investor_type 52 | return affliate_type 53 | -------------------------------------------------------------------------------- /data/whale_eth_tx_data.py: -------------------------------------------------------------------------------- 1 | from .html_helper import get_html_by_url 2 | 3 | affliate_type = "affliate" 4 | personal_type = "personal" 5 | exchange_type = 'exchange' 6 | bfs_max_depth = 5 7 | 8 | def get_total_number_of_page(soup): 9 | try: 10 | span = soup.find('p',{'align':"right"}) 11 | d_s = span.findAll("b") 12 | return int(d_s[1].text) 13 | except: 14 | return 1 15 | 16 | def identify_investor_type_helper(soup): 17 | if soup is None: 18 | return [] 19 | no_matching_exist = soup.find("font",{"color":"black"}) 20 | if no_matching_exist is not None: 21 | return "affliate" 22 | trs = soup.findAll("tr") 23 | tx_arr = [] 24 | for tr_index,tr in enumerate(trs): 25 | if tr_index != 0: 26 | # print("tr_index:{}".format(tr_index)) 27 | tds = tr.findAll("td") 28 | for td_index,td in enumerate(tds): 29 | if td_index == 4: 30 | # type 31 | tx_type = td.find('span').text 32 | elif td_index == 5: 33 | # to_Address 34 | m_a = td.find("a") 35 | if m_a is None: 36 | m_a = td.find("span") 37 | to_address = m_a.text 38 | to_address = to_address.lower() 39 | tx_arr.append([tx_type,to_address]) 40 | for tx in tx_arr: 41 | [tx_type,to_address] = tx 42 | if tx_type == "OUT" and (("auction" in to_address) or ("sale" in to_address) or ("etherdelta" in to_address)): 43 | return personal_type 44 | if tx_type == "OUT" and (("binance" in to_address) or ("liqui" in to_address) or ("bittrex" in to_address)): 45 | return exchange_type 46 | return affliate_type 47 | 48 | def identify_investor_type(account): 49 | url = "http://etherscan.io/txs?a={}&mode".format(account) 50 | soup = get_html_by_url(url) 51 | total_number_of_page = get_total_number_of_page(soup) 52 | print("\t\t[identify_investor_type] ETH : {} for {}".format(total_number_of_page,account)) 53 | 54 | investor_type = identify_investor_type_helper(soup) 55 | if investor_type != affliate_type: return investor_type 56 | 57 | for i in range(2,min(bfs_max_depth+1,total_number_of_page+1)): 58 | print("\t\t{}".format(i)) 59 | soup = get_html_by_url("{}&p={}".format(url,i)) 60 | investor_type = identify_investor_type_helper(soup) 61 | if investor_type != affliate_type: return investor_type 62 | return affliate_type 63 | -------------------------------------------------------------------------------- /data/html_helper.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | from bs4 import BeautifulSoup 3 | import cfscrape 4 | 5 | 6 | def parse_date(target_data): 7 | target_data = target_data.replace(minute=0, hour=0, second=0, microsecond=0) 8 | return target_data 9 | 10 | def get_today(): 11 | return parse_date(datetime.datetime.utcnow()) 12 | 13 | url_content_cache = dict() 14 | 15 | def get_html_by_url(url): 16 | if url not in url_content_cache: 17 | scraper = cfscrape.create_scraper() 18 | html = scraper.get(url).content 19 | soup = BeautifulSoup(html,"lxml") 20 | url_content_cache[url] = soup 21 | else: 22 | soup = url_content_cache[url] 23 | return soup 24 | 25 | def find_token_tx_helper(url): 26 | soup = get_html_by_url(url) 27 | if soup is None: 28 | return [] 29 | trs = soup.findAll("tr") 30 | tx_arr = [] 31 | for tr_index,tr in enumerate(trs): 32 | if tr_index != 0: 33 | # print("tr_index:{}".format(tr_index)) 34 | tds = tr.findAll("td") 35 | for td_index,td in enumerate(tds): 36 | if td_index == 1: 37 | # from_Address 38 | m_a = td.find("a") 39 | if m_a is None: 40 | m_a = td.find("span") 41 | from_address = m_a.text 42 | # print("from_address:{}".format(from_address)) 43 | from_address = from_address.lower() 44 | tx_arr.append(from_address) 45 | return tx_arr 46 | 47 | def get_total_number_of_page(url): 48 | soup = get_html_by_url(url) 49 | try: 50 | d_s = soup.findAll("b") 51 | return int(d_s[1].text) 52 | except: 53 | return 1 54 | 55 | def find_tx_given_token_contract_address(contract_address,start_page,end_page): 56 | url = "https://etherscan.io/token/generic-tokenholders2?a={}".format(contract_address) 57 | total_number_of_page = get_total_number_of_page(url) 58 | tx_arrs = [] 59 | print("total_number_of_page:{} for {}".format(total_number_of_page,contract_address)) 60 | for i in range(start_page,end_page+1): 61 | print(i) 62 | tx_arr = find_token_tx_helper("{}&p={}".format(url,i)) 63 | tx_arrs = tx_arrs + tx_arr 64 | return (tx_arrs,total_number_of_page) 65 | 66 | def check_if_address_name_exists(account): 67 | url = "https://etherscan.io/address/{}".format(account) 68 | soup = get_html_by_url(url) 69 | name = soup.find("font",{"title":"NameTag"}) 70 | if name is not None: 71 | return name.text 72 | else: 73 | return "" 74 | -------------------------------------------------------------------------------- /analysis/calculate_holding_amount.py: -------------------------------------------------------------------------------- 1 | import sys 2 | sys.path.insert(0,'..') 3 | from data.whale_data import exchnage_accounts 4 | from data.html_helper import check_if_address_name_exists 5 | from data.whale_eth_tx_data import * 6 | from data.whale_token_tx_data import identify_investor_type_token 7 | 8 | holding_account = "holding_account" 9 | deposit_account = 'deposit_account' 10 | withdraw_account = "withdraw_account" 11 | 12 | in_type = "IN" 13 | out_type = "OUT" 14 | 15 | all_acc_types = dict() 16 | for acc in exchnage_accounts: 17 | all_acc_types[acc] = exchange_type 18 | 19 | 20 | def update_y_array(X,y,timestamp,amount): 21 | target_index = 0 22 | for i in range(len(X)): 23 | x_time = X[i] 24 | if timestamp < x_time: 25 | target_index = i 26 | break 27 | 28 | for i in range(target_index,len(y)): 29 | y[i] += amount 30 | 31 | return y 32 | 33 | def perform_bfs_on_accounts(out_txs,top_holder_type,acc,m_type='OUT'): 34 | print("\t"+m_type) 35 | unique_out = set() 36 | for out in out_txs: 37 | unique_out.add(out[3]) 38 | unique_out = list(unique_out)[:5] 39 | for out in unique_out: 40 | print("\t"+out) 41 | if out not in all_acc_types: 42 | investor_type = identify_investor_type(out) 43 | if investor_type == affliate_type: 44 | investor_type = identify_investor_type_token(out) 45 | print("\t\t{}".format(investor_type)) 46 | else: 47 | investor_type = all_acc_types[out] 48 | if investor_type == exchange_type: 49 | top_holder_type[acc] = deposit_account if m_type == "OUT" else withdraw_account 50 | all_acc_types[out] = investor_type 51 | 52 | if acc not in top_holder_type: 53 | top_holder_type[acc] = holding_account 54 | 55 | return top_holder_type 56 | 57 | def calculate_holding_amount(X,escape_accounts,txs): 58 | top_holder_type = dict() 59 | 60 | for acc in txs: 61 | tx = txs[acc] 62 | 63 | if acc in escape_accounts: 64 | continue 65 | 66 | #如果当前账户从来没有向外打过token,ignore 67 | out_txs = [item for item in tx if item[2] == 'OUT'] 68 | if len(out_txs) == 0: 69 | print("\tholding account") 70 | top_holder_type[acc] = holding_account 71 | continue 72 | 73 | # build all traxe Y: holding_amount, deposit_amount, withdraw_amount 74 | amount_trace_y = [0] * len(X) 75 | 76 | for holder in txs: 77 | if holder in escape_accounts: 78 | continue 79 | if holder not in top_holder_type: 80 | print("{} not identified! ".format(holder)) 81 | continue 82 | 83 | holder_type = top_holder_type[holder] 84 | holder_txs = txs[holder] 85 | print("{} {}".format(holder,holder_type)) 86 | 87 | for tx in holder_txs: 88 | [timestamp,from_a,tx_type,to_a,amount] = tx 89 | if holder_type == holding_account: 90 | if tx_type == in_type: 91 | amount_trace_y = update_y_array(X,amount_trace_y,timestamp,amount) 92 | else: 93 | amount_trace_y = update_y_array(X,amount_trace_y,timestamp,-amount) 94 | 95 | return amount_trace_y 96 | -------------------------------------------------------------------------------- /analysis/calculate_historical.py: -------------------------------------------------------------------------------- 1 | import operator 2 | import sys 3 | sys.path.insert(0,'..') 4 | 5 | in_type = "IN" 6 | out_type = "OUT" 7 | 8 | def calculate_historical_holders(txs,X): 9 | 10 | # init acc_holding_values_dict 11 | acc_holding_values_dict = dict() 12 | dummy_acc_dict = dict() 13 | for acc in txs: 14 | dummy_acc_dict[acc] = 0 15 | for t in X: 16 | acc_holding_values_dict[t] = dummy_acc_dict.copy() 17 | 18 | # populate acc_holding_values_dict 19 | for acc in txs: 20 | a_txs = txs[acc] 21 | for m_tx in a_txs: 22 | [t,from_a,tx_type,to_a,quantity] = m_tx 23 | for x_t in X: 24 | if x_t >= t: 25 | t_dict = acc_holding_values_dict[x_t] 26 | if tx_type == in_type: 27 | t_dict[to_a] += quantity 28 | else: 29 | t_dict[from_a] -= quantity 30 | 31 | acc_holding_values_dict[x_t] = t_dict 32 | 33 | return acc_holding_values_dict 34 | 35 | def find_top_50_over_time_helper(acc_holding_values_dict): 36 | top_50_holding_values = dict() 37 | for t in acc_holding_values_dict: 38 | acc_list_at_time_t = acc_holding_values_dict[t] 39 | sorted_x = sorted(acc_list_at_time_t.items(), key=operator.itemgetter(1),reverse=True)[:50] 40 | 41 | sorted_dict = dict() 42 | for key, value in sorted_x: 43 | sorted_dict[key] = float(value) 44 | top_50_holding_values[t] = sorted_dict 45 | 46 | return top_50_holding_values 47 | 48 | def calculate_top_50_token_moving_average(top_50_holding_values): 49 | top_50_token_moving_average_trace = [] 50 | for t in top_50_holding_values: 51 | curr_sum = 0.0 52 | accs = top_50_holding_values[t] 53 | for acc in accs: curr_sum += accs[acc] 54 | curr_sum /= 50 55 | top_50_token_moving_average_trace.append(curr_sum) 56 | return top_50_token_moving_average_trace 57 | 58 | def calculate_top_50_list_and_token_amount_change(top_50_holding_values,escape_accounts,is_exchange=False): 59 | top_50_list_and_token_amount_change_trace = [] 60 | unique_acc_set = set() 61 | 62 | for t in top_50_holding_values: 63 | accs = top_50_holding_values[t] 64 | for acc in accs: 65 | if not is_exchange and acc in escape_accounts: 66 | continue 67 | if is_exchange and acc not in escape_accounts: 68 | continue 69 | if acc not in unique_acc_set: 70 | unique_acc_set.add(acc) 71 | top_50_list_and_token_amount_change_trace.append({"name":acc,'x':[t],'y':[accs[acc]]}) 72 | else: 73 | for json in top_50_list_and_token_amount_change_trace: 74 | if json['name'] == acc: 75 | json['x'].append(t) 76 | json['y'].append(accs[acc]) 77 | 78 | if is_exchange: 79 | new_return = [] 80 | for json in top_50_list_and_token_amount_change_trace: 81 | y = json['y'] 82 | if y[-1] !=0: 83 | new_return.append(json) 84 | top_50_list_and_token_amount_change_trace = new_return 85 | return top_50_list_and_token_amount_change_trace 86 | # def calculate_top_50_holding_token_amount(acc_holding_values_dict): 87 | -------------------------------------------------------------------------------- /data/whale_operation.py: -------------------------------------------------------------------------------- 1 | if __name__ == "__main__": 2 | from html_helper import get_total_number_of_page,get_html_by_url 3 | else: 4 | from .html_helper import get_total_number_of_page,get_html_by_url 5 | from dateutil import parser 6 | from joblib import Parallel, delayed 7 | import multiprocessing 8 | num_cores = int(multiprocessing.cpu_count() * 2 / 3) 9 | 10 | def find_whale_tx_helper(url): 11 | soup = get_html_by_url(url) 12 | if soup is None: 13 | return [] 14 | no_matching_exist = soup.find("font",{"color":"black"}) 15 | if no_matching_exist is not None: 16 | return [] 17 | trs = soup.findAll("tr") 18 | tx_arr = [] 19 | for tr_index,tr in enumerate(trs): 20 | if tr_index != 0: 21 | # print("tr_index:{}".format(tr_index)) 22 | tds = tr.findAll("td") 23 | for td_index,td in enumerate(tds): 24 | if td_index == 1: 25 | #timestamp 26 | timestamp_s = td.find('span')['title'] 27 | timestamp = parser.parse(timestamp_s) 28 | elif td_index == 2: 29 | # from_Address 30 | m_a = td.find("a") 31 | if m_a is None: 32 | m_a = td.find("span") 33 | from_address = m_a.text 34 | # print("from_address:{}".format(from_address)) 35 | elif td_index == 3: 36 | # type 37 | tx_type = td.find("span").text 38 | if "IN" in tx_type: 39 | tx_type = "IN" 40 | # print("tx_type:{}".format(tx_type)) 41 | elif td_index == 4: 42 | # to_Address 43 | m_a = td.find("a") 44 | if m_a is None: 45 | m_a = td.find("span") 46 | to_address = m_a.text 47 | # print("to_address:{}".format(to_address)) 48 | elif td_index == 5: 49 | quantity = float(td.text.replace(",","")) 50 | # print("quantity:{}".format(quantity)) 51 | from_address = from_address.lower() 52 | to_address = to_address.lower() 53 | tx_arr.append([timestamp,from_address,tx_type,to_address,quantity]) 54 | print("{} {}".format(len(tx_arr),url)) 55 | return tx_arr 56 | 57 | def find_whale_txs(token_address,contract_address,start_page=1,end_page=500): 58 | url = "http://etherscan.io/token/generic-tokentxns2?contractAddress={}&a={}&mode=".format(token_address,contract_address) 59 | total_number_of_page = get_total_number_of_page(url) 60 | print("\t\ttotal_number_of_page:{} for {}".format(total_number_of_page,contract_address)) 61 | end_page = min(total_number_of_page,end_page) 62 | if end_page - start_page > 10: 63 | results = Parallel(n_jobs=num_cores)(delayed(find_whale_tx_helper)("{}&p={}".format(url,i)) for i in range(start_page,end_page+1)) 64 | else: 65 | results = [find_whale_tx_helper("{}&p={}".format(url,i)) for i in range(start_page,end_page+1)] 66 | tx_arrs = [] 67 | for result in results: 68 | tx_arrs += result 69 | return tx_arrs 70 | 71 | if __name__ == "__main__": 72 | results = find_whale_txs('0x255aa6df07540cb5d3d297f0d0d4d84cb52bc8e6','0x0a690f134c0f7deec5e70dc56793a00c3624c0a0',1,5) 73 | print(results) 74 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | from analysis.bussiness_logic import main_business_logic 2 | import data.whale_data as wd 3 | 4 | #exchange_accounts 5 | exchange_accounts = ['0xfbb1b73c4f0bda4f67dca266ce6ef42f520fbb98','0xe94b04a0fed112f3664e45adb2b8915693dd5ff3', #Bittrex 6 | '0x1151314c646ce4e0efd76d1af4760ae66a9fe30f','0xcafb10ee663f465f9d10588ac44ed20ed608c11e','0x7180EB39A6264938FDB3EfFD7341C4727c382153', '0x7727e5113d1d161373623e5f49fd568b4f543a9e','0x4fdd5eb2fb260149a3903859043e962ab89d8ed4',#Bitfinex 7 | '0x3f5ce5fbfe3e9af3971dd833d26ba9b5c936f0be', #Binance 8 | '0xb794f5ea0ba39494ce839613fffba74279579268','0x32be343b94f860124dc4fee278fdcbd38c102d88','0xab11204cfeaccffa63c2d23aef2ea9accdb0a0d5','0x209c4784ab1e8183cf58ca33cb740efbf3fc18ef','0x0536806df512D6cDDE913Cf95c9886f65b1D3462',#poloniex 9 | '0x5E575279bf9f4acf0A130c186861454247394C06','0x8271B2E8CBe29396e9563229030c89679B9470db', #liqui.io] 10 | '0x8d12a197cb00d4747a1fe03395095ce2a5cc6819', #etherdelta 11 | '0xfaabe432a1e6843f3486f11fa360a1e1248677fc', #cex.io 12 | '0x5c985e89dde482efe97ea9f1950ad149eb73829b','0x58c2cb4a6BeE98C309215D0d2A38d7F8aa71211c','0xeec606a66edb6f497662ea31b5eb1610da87ab5f','0x04645AF26b54BD85Dc02Ac65054e87362A72CB22','0xE93381fB4c4F14bDa253907b18faD305D799241a' #huobi.pro 13 | ] 14 | exchange_accounts = [x.lower() for x in exchange_accounts] 15 | 16 | 17 | def main_func(symbol,Watch_addr,coinmarketcap_symbol): 18 | wd.Watch_addr = Watch_addr 19 | wd.exchnage_accounts = exchange_accounts 20 | 21 | escape_accounts = list(exchange_accounts) 22 | escape_accounts.append("0xead6be34ce315940264519f250d8160f369fa5cd") #ZRX bot 23 | escape_accounts.append("0x00c7122633a4ef0bc72f7d02456ee2b11e97561e") #Raidne Team 24 | escape_accounts.append("0x615ed6779507f223d04722d43ccc0cd871964e2a") #GTO Team 25 | escape_accounts.append("0xe16fd9b95758fe8f3a478ef9b750a64513bf2e80") # ICX team) 26 | escape_accounts.append("0x6863424c64081d69a9f23f780bb3c73dddbf15f6") # THETA team) 27 | escape_accounts.append("0x30993352B0e5a02A5ea2A7711FD7C97a4401D654") # Theta team 28 | escape_accounts.append("0x03E130eaFAB61ca4D31923B4043db497a830D2bD") # Theta team 29 | escape_accounts.append("0xb65Ad53b13B3c7a89527164Fe69CcD56FcEba1B9") # theta team 30 | 31 | return main_business_logic(symbol,escape_accounts,coinmarketcap_symbol) 32 | 33 | def driver_to_run_main_func(data): 34 | plot_dict = dict() 35 | 36 | for symbol in data: 37 | coin_symbol,Watch_addr = data[symbol] 38 | print("{} {} {}".format(symbol,coin_symbol,Watch_addr)) 39 | plots = main_func(symbol,Watch_addr,coin_symbol) 40 | plot_dict[symbol] = plots 41 | 42 | for symbol in plot_dict: 43 | coin_symbol,Watch_addr = data[symbol] 44 | print("{} {} {}".format(symbol,coin_symbol,Watch_addr)) 45 | plots = plot_dict[symbol] 46 | for plot in plots: 47 | print("\t{} {}\n".format(plot,plots[plot])) 48 | import time 49 | if __name__ == "__main__": 50 | data = { 51 | # "OMG": ('omisego','0xd26114cd6EE289AccF82350c8d8487fedB8A0C07'), 52 | # 'SNT': ('status','0x744d70fdbe2ba4cf95131626614a1763df805b9e'), 53 | # 'CVC': ('civic','0x41e5560054824ea6b0732e656e3ad64e20e94e45'), 54 | # 'MTL': ('metal','0xF433089366899D83a9f26A773D59ec7eCF30355e'), 55 | # 'ADX': ('adx-net','0x4470BB87d77b963A013DB939BE332f927f2b992e'), 56 | # 'KNC': ('kyber-network','0xdd974d5c2e2928dea5f71b9825b8b646686bd200'), 57 | # 'ZRX': ('0x','0xe41d2489571d322189246dafa5ebde1f4699f498'), 58 | # 'RDN': ('raiden-network-token','0x255aa6df07540cb5d3d297f0d0d4d84cb52bc8e6') 59 | #'GTO' : ('gifto','0xc5bbae50781be1669306b9e001eff57a2957b09d') 60 | 'THETA' : ('theta-token','0x3883f5e181fccaF8410FA61e12b59BAd963fb645') 61 | } 62 | driver_to_run_main_func(data) 63 | -------------------------------------------------------------------------------- /analysis/bussiness_logic.py: -------------------------------------------------------------------------------- 1 | import sys 2 | sys.path.insert(0,'..') 3 | from data.whale_data import find_exchange_txs 4 | from plot.plotly_helper import plot_using_plotly 5 | from .calculate_holding_amount import calculate_holding_amount 6 | from .calculate_historical import * 7 | from .coinmarketcap_draw import coinmarketcap_data 8 | from data.whale_data import find_whale_account_token_tx,find_interstering_accounts 9 | from .holder_tracking import track_holder_number_over_time 10 | 11 | in_type = "IN" 12 | out_type = "OUT" 13 | 14 | # 保存最早的交易时间 15 | from datetime import datetime 16 | from datetime import timedelta 17 | the_earliest_tx_time = datetime.now() 18 | 19 | def update_y_array(X,y,timestamp,amount): 20 | target_index = 0 21 | for i in range(len(X)): 22 | x_time = X[i] 23 | if timestamp < x_time: 24 | target_index = i 25 | break 26 | 27 | for i in range(target_index,len(y)): 28 | y[i] += amount 29 | 30 | return y 31 | 32 | def update_y_daily_array(X,y,timestamp,amount): 33 | target_index = 0 34 | for i in range(len(X)): 35 | x_time = X[i] 36 | if timestamp < x_time: 37 | target_index = i 38 | break 39 | y[target_index] += amount 40 | return y 41 | 42 | def main_business_logic(symbol,escape_accounts,coinmarketcap_symbol): 43 | txs = find_exchange_txs() 44 | 45 | # 找到最早的交易时间 46 | global the_earliest_tx_time 47 | 48 | for acc in txs: 49 | print(acc) 50 | 51 | acc_txs = txs[acc] 52 | for sub_tx in acc_txs: 53 | tx_date = sub_tx[0] 54 | if tx_date < the_earliest_tx_time: the_earliest_tx_time = tx_date 55 | 56 | # build X time array 57 | the_earliest_tx_time = the_earliest_tx_time.replace(minute=0, second=0) 58 | current_time = datetime.now().replace(minute=0, second=0) 59 | tmp_time = the_earliest_tx_time 60 | X = [] 61 | 62 | while tmp_time < current_time: 63 | X.append(tmp_time) 64 | tmp_time += timedelta(hours=1) 65 | print(len(X)) 66 | 67 | # build all traxe Y: deposit_amount, withdraw_amount 68 | deposit_trace_y = [0] * len(X) 69 | withdraw_trace_y = [0] * len(X) 70 | exchange_remain_amount_y = [0] * len(X) 71 | 72 | # Daily Stat 73 | deposit_daily_trace_y = [0] * len(X) 74 | withdraw_daily_trace_y = [0] * len(X) 75 | exchange_daily_remain_amount_y = [0] * len(X) 76 | 77 | for holder in txs: 78 | holder_txs = txs[holder] 79 | 80 | for tx in holder_txs: 81 | [timestamp,from_a,tx_type,to_a,amount] = tx 82 | if tx_type == in_type: 83 | deposit_trace_y = update_y_array(X,deposit_trace_y,timestamp,amount) 84 | deposit_daily_trace_y = update_y_daily_array(X,deposit_daily_trace_y,timestamp,amount) 85 | else: 86 | withdraw_trace_y = update_y_array(X,withdraw_trace_y,timestamp,amount) 87 | withdraw_daily_trace_y = update_y_daily_array(X,withdraw_daily_trace_y,timestamp,amount) 88 | 89 | for i in range(0,len(X)): 90 | exchange_remain_amount_y[i] = deposit_trace_y[i] - withdraw_trace_y[i] 91 | exchange_daily_remain_amount_y[i] = deposit_daily_trace_y[i] - withdraw_daily_trace_y[i] 92 | 93 | # Draw the kline data from coinmarketcap 94 | df,df_date = coinmarketcap_data(coinmarketcap_symbol) 95 | price_trace = {'x':df_date,'y':df['price_usd'].values.tolist(),'name':"Price(USD)","yaxis":'y2'} 96 | volume_trace = {'x':df_date,'y':df['volume_token'].values.tolist(),'name':"Trading Volume(Token)"} 97 | 98 | deposit_trace = {"x":X,"y":deposit_trace_y,"name":"Exchange Deposit Amount(Token)"} 99 | withdraw_trace = {"x":X,"y":withdraw_trace_y,"name":"Exchange Withdraw Amount(Token)"} 100 | exchange_remain_amount_trace = {"x":X,"y":exchange_remain_amount_y,"name":"Exchange Remain Amount(Token)"} 101 | 102 | whale_txs = find_whale_account_token_tx(escape_accounts,1,1) 103 | # current_top_50_holding_amount_y = calculate_holding_amount(X,escape_accounts,whale_txs) 104 | # holding_amount_trace = {"x":X,"y":current_top_50_holding_amount_y,"name":"Top 50 {} Holder Holding Amount(Token)".format(symbol)} 105 | first_plot = plot_using_plotly("Total {} Exchange Analysis (Bittrex, Bitfinex, Binance, Poloniex,liqui.io, Etherdelta, huobi.pro, CEX.com)".format(symbol), 106 | [deposit_trace,withdraw_trace,exchange_remain_amount_trace,price_trace,volume_trace], 107 | '{} Total'.format(symbol)) 108 | 109 | all_whale_txs = find_whale_account_token_tx(escape_accounts,2,2) 110 | for acc in whale_txs: 111 | if acc not in all_whale_txs: 112 | all_whale_txs[acc] = whale_txs[acc] 113 | acc_holding_values_dict = calculate_historical_holders(all_whale_txs,X) 114 | top_50_holding_values = find_top_50_over_time_helper(acc_holding_values_dict) 115 | top_50_list_and_token_amount_change_trace = calculate_top_50_list_and_token_amount_change(top_50_holding_values,escape_accounts) 116 | top_50_list_and_token_amount_change_trace.append(price_trace) 117 | plot_top_50_token_amount = plot_using_plotly("Top 50 List and their token amount (without counting the exchange)",top_50_list_and_token_amount_change_trace,'{} top 50'.format(symbol)) 118 | 119 | top_50_token_moving_average = calculate_top_50_token_moving_average(top_50_holding_values) 120 | top_50_token_moving_average_trace = {"x":X,"y":top_50_token_moving_average,"name":"Top 50 Token MA"} 121 | top_50_token_ma_trace = plot_using_plotly("Top 50 Token amount Moving Average (without counting the exchange)",[top_50_token_moving_average_trace,price_trace],'{} Top 50 MA'.format(symbol)) 122 | 123 | exchange_holding_values_dict = calculate_historical_holders(txs,X) 124 | exchange_holding_values = find_top_50_over_time_helper(exchange_holding_values_dict) 125 | exchange_token_moving_average_trace = calculate_top_50_token_moving_average(exchange_holding_values) 126 | exchange_list_and_token_amount_change_trace = calculate_top_50_list_and_token_amount_change(exchange_holding_values,escape_accounts,is_exchange=True) 127 | exchange_list_and_token_amount_change_trace.append(price_trace) 128 | exchange_plot = plot_using_plotly("Exchange token amount",exchange_list_and_token_amount_change_trace,'{} exchange token amount'.format(symbol)) 129 | 130 | deposit_trace = {"x":X,"y":deposit_daily_trace_y,"name":"Exchange Deposit Amount(Token)"} 131 | withdraw_trace = {"x":X,"y":withdraw_daily_trace_y,"name":"Exchange Withdraw Amount(Token)"} 132 | exchange_daily_remain_amount_trace = {"x":X,"y":exchange_daily_remain_amount_y,"name":"Exchange Daily Remain Amount(Token)"} 133 | second_plot = plot_using_plotly("Hourly {} Exchange Analysis (Bittrex, Bitfinex, Binance, Poloniex,liqui.io, Etherdelta, huobi.pro, CEX.com)".format(symbol),[deposit_trace,exchange_daily_remain_amount_trace,price_trace],'{} hourly'.format(symbol)) 134 | 135 | _,total_number_of_pages = find_interstering_accounts() 136 | remaining_whale_data = find_whale_account_token_tx(escape_accounts,3,total_number_of_pages) 137 | for acc in remaining_whale_data: 138 | if acc not in all_whale_txs: 139 | all_whale_txs[acc] = remaining_whale_data[acc] 140 | acc_holding_values_dict = calculate_historical_holders(all_whale_txs,X) 141 | track_holder_number_over_time_y = track_holder_number_over_time(acc_holding_values_dict) 142 | track_holder_number_over_time_trace = {"x":X,"y":track_holder_number_over_time_y,"name":"Token Holder"} 143 | plot_track_holder_number_over_time = plot_using_plotly("{} Token Holder Trace Overtime".format(symbol),[track_holder_number_over_time_trace,price_trace],'{} Holder'.format(symbol)) 144 | 145 | return ({"total_analysis":first_plot,"hourly analysis":second_plot,"plot_top_50_token_amount":plot_top_50_token_amount,"exchange_plot":exchange_plot,"top_50_token_ma_trace":top_50_token_ma_trace, "plot_track_holder_number_over_time":plot_track_holder_number_over_time}) 146 | --------------------------------------------------------------------------------