Last active
January 11, 2024 05:55
-
-
Save bmatthewshea/90b120722e0561dd235adcdc231b6765 to your computer and use it in GitHub Desktop.
Python script to calculate time to win full block and revenue per day. (GPU MINING)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/python | |
# -*- coding: utf-8 -*- | |
# "Whattomine GPU Hash time calculator" | |
# | |
# This is not for CPU or ASIC calculations. | |
# Author: Brady Shea | |
# Email: Use github user: bmatthewshea or gist comments | |
# Origin: https://gist.github.com/bmatthewshea/90b120722e0561dd235adcdc231b6765 | |
# | |
# If this script helps you, please consider a small donation! | |
# | |
# BTC 1CmuTLFoApWRxXRaZvpsQ1cC9gdJSef3K6 | |
# ETH 4e06e4080E6043dfF23c8b634712Aca2e1DE4347 | |
# LTC LawZKPgkUc1DJRjdMqH2u8L6cgSMpD8H2E | |
# XMR 49mRDhPU1qURWT5g9ZPuexELwSr1VUpKMQMPRHTkkezXG3eQ1rHYedkieb7RkzPkVrW9QPm3r6cLA1AuJEDKLrqzRFPyZ79 | |
# XVG/Verge D64XkTk9YnR2HX3CupZWxWnWwC3TVDRt3T | |
# FTC/FeatherCoin 6sStepeFZg3o4VYb2HRNJmw2sRnfVJhmdQ | |
# Thank you! | |
# Currently the script calculates on many coin types/algos but is more accurate on ethash: | |
# (ETH, ETC, EXP, PIRL, UBIQ, ELLA, MUSIC, SOIL, etc) neoscript, lyra, cryptonight/note should also be accurate | |
# THIS IS A WORK IN PROGRESS - EXCUSE THE MESS. | |
# Non-GPU minable asic algos (SHA-256 + any others) hashtimes are displayed as if using a GPU (In TH). | |
# In other words, they are shown just for fun. | |
import json, requests, collections, math, argparse, sys | |
import urllib3 | |
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) | |
# You can adjust this however you want: | |
minerapi = 'https://whattomine.com/calculators.json' | |
priceapi = 'https://api.coindesk.com/v1/bpi/currentprice.json' | |
speeds = ['10.0', '15.0', '20.0', '25.0', '30.0', '50.0', '100.0', '250.0', '300.0', '350.0', '600.0', '650.0', '750.0', '1000.0', '2000.0', '3000.0', '4000.0', '5000.0', '10000.0'] | |
heading0 = "Note: Ethash, equihash, neoscript, cryptonight and lyra calculations are more precise than others.\n The times below are 'averages'. Seconds are at 1.0/100% variance to win a full block." | |
heading1 = "Speed Secs to win block Time to win block Coins/Day - USD Revenue" | |
heading2 = "========== ======================== ======================== =======================" | |
# Dictionary for modifiers on hash calculation and text labels for hash speed | |
dModifiers = {} | |
# Lower means longer times | |
dModifiers ={ | |
'SHA-256': {'modifier': 0.98e12, 'dHlabel': "TH" }, \ | |
'Skunkhash': {'modifier': 616666.0, 'dHlabel': "MH" }, \ | |
'Pascal': {'modifier': 22999977.0, 'dHlabel': "MH" }, \ | |
'LBRY': {'modifier': 4499995.0, 'dHlabel': "MH" }, \ | |
'Groestl': {'modifier': 616666.0, 'dHlabel': "MH" }, \ | |
'X11Gost': {'modifier': 229999.7, 'dHlabel': "MH" }, \ | |
'Blake2b': {'modifier': 32999967.0, 'dHlabel': "MH" }, \ | |
'Blake14r': {'modifier': 44999955.0, 'dHlabel': "MH" }, \ | |
'NeoScrypt': {'modifier': 1192.0, 'dHlabel': "KH" }, \ | |
'Lyra2REv2': {'modifier': 1e6, 'dHlabel': "MH" }, \ | |
'Equihash': {'modifier': 0.000235, 'dHlabel': "H" }, \ | |
'TimeTravel10': {'modifier': 1e6, 'dHlabel': "MH" }, \ | |
'Xevan': {'modifier': 1e3, 'dHlabel': "KH" }, \ | |
'CryptoNight': {'modifier': 1, 'dHlabel': "H" }, \ | |
'CryptoNightV7': {'modifier': 1, 'dHlabel': "H" }, \ | |
'Ethash': {'modifier': 1e6, 'dHlabel': "MH" }, \ | |
'Etchash': {'modifier': 1e6, 'dHlabel': "MH" }, \ | |
'Autolykos': {'modifier': 1e6, 'dHlabel': "MH" }, \ | |
'Ubqhash': {'modifier': 1e6, 'dHlabel': "MH" } | |
} | |
# So far: ETHASH, CNIGHT, NEOSCRIPT, LYRA2, TimeTravel should be precise/done | |
# Add some flags for command line and parse | |
parser = argparse.ArgumentParser(description='Grab crypto-coin data and do block time calculations.') | |
parser.add_argument('-m', '--manual', action='store_true', help='Enter current blocktime and nethash rate manually') | |
parser.add_argument('-n', '--name', help='Crypto-coin full name. Example: Ethereum') | |
parser.add_argument('-s', '--symbol', help='Crypto-coin symbol name. Example: ETH') | |
args = parser.parse_args() | |
# label,seconds conversions | |
intervals = ( | |
('years', 31557600), # 60 * 60 * 24 * 7 * 52.143.... <- using astronomical secs in year | |
('weeks', 604800), # 60 * 60 * 24 * 7 | |
('days', 86400), # 60 * 60 * 24 | |
('hours', 3600), # 60 * 60 | |
('minutes', 60), | |
('seconds', 1), | |
) | |
#console colorcode | |
SETRED = '\033[31m' | |
SETLRED = '\033[91m' | |
SETGREEN = '\033[32m' | |
SETLGREEN = '\033[92m' | |
SETYELLOW = '\033[33m' | |
SETLYELLOW = '\033[93m' | |
SETBLUE = '\033[34m' | |
SETLBLUE = '\033[94m' | |
SETMAGENTA = '\033[35m' | |
SETLMAGENTA = '\033[95m' | |
SETCYAN = '\033[36m' | |
SETLCYAN = '\033[96m' | |
SETGREY = '\033[37m' | |
SETLGREY = '\033[97m' | |
RESET_COLOR = '\033[0m' | |
# DEFs | |
def setcolor(colorcodestr, str): | |
color_line = colorcodestr + str + RESET_COLOR | |
return color_line | |
#print (SETBLUE, "Test color.") | |
# (default time granularity =2 fields) - https://stackoverflow.com/a/24542445/503621 | |
def display_time(seconds, granularity=2): | |
result = [] | |
for name, count in intervals: | |
value = seconds // count | |
if value: | |
seconds -= value * count | |
if value == 1: | |
name = name.rstrip('s') | |
result.append("{} {}".format(value, name)) | |
return ', '.join(result[:granularity]) | |
# Create a keypaths for reverse dictionary call | |
def keypaths(nested): | |
for key, value in nested.items(): | |
if isinstance(value, collections.abc.Mapping): | |
for subkey, subvalue in keypaths(value): | |
yield [key] + subkey, subvalue | |
else: | |
yield [key], value | |
def printTimeTable(speed, label, hashtime, coinsperday, revenue): | |
print("{0:<7}{1:>1} {2:>25} {3:>25} {4:8.8f} - ${5:.2f}".format(speed, " " + label, str(int(hashtime)) + " seconds", display_time(int(hashtime)), coinsperday, revenue)) | |
return | |
def setlabelmodifier(labelarg): #manual entry label/modifier | |
if labelarg == "K": | |
imod=1000 | |
if labelarg == "M": | |
imod=1e6 | |
if labelarg == "G": | |
imod=1e9 | |
if labelarg == "T": | |
imod=1e12 | |
if labelarg == "H": | |
imod=1 | |
else: # append H on others | |
labelarg += "H" | |
return labelarg, imod | |
def getuserspeed(): # ask for if user wants table output or specific speed? | |
while True: | |
try: | |
userspeed = float(input(" Please enter a mining speed to calculate or <Enter> for default table speeds (30.0 = 30MH): ")) | |
except (ValueError, NameError, SyntaxError): | |
userspeed = 0.0 # figure it's non-float/int or <enter> | |
eraseline() # just remove line since it's ignored anyway | |
break | |
else: # needs some validation - assume it's valid int - break and return it: | |
break | |
return userspeed | |
def eraseline(): | |
CURSOR_UP_ONE = '\x1b[1A' | |
ERASE_LINE = '\x1b[2K' | |
print(CURSOR_UP_ONE + ERASE_LINE) | |
# MANUAL LOOKUP | |
if args.manual: | |
print("Manual entry:\nNote that calculating using difficulty isn't very accurate, but you need less input.\nWorks best on ETHash type coins and not at all on others.\n") | |
sCalctype = "" | |
while not sCalctype or sCalctype not in ('d', 'n'): | |
sCalctype = str(raw_input(" Calculate using (d)ifficulty or (n)etwork hashrate: ")) | |
if sCalctype == "d": | |
while True: | |
try: | |
iDifficulty = int(input(" Enter Difficulty (Example: 3242258038793980): ")) | |
except (ValueError, NameError): | |
print("Not a number. Try again.") | |
continue | |
else: | |
break | |
if iDifficulty <= 10000: | |
print("The difficulty entered is too low to calculate.") | |
sys.exit() | |
# ask if user wants table output or specific speed? (enter does table) | |
fOutputspeed = 0.0 | |
fOutputspeed = getuserspeed() | |
sHashlabel = "" | |
while not sHashlabel or sHashlabel not in ('h', 'k', 'm', 'g', 't'): | |
sHashlabel = str(raw_input(" Enter output speed ('h' = Hashes', 'k' = KH, 'm' = MH, 'g' = GH, 't' = TH): ")) | |
sHashlabel = sHashlabel.upper() | |
sHashlabel, iMultiplier = setlabelmodifier(sHashlabel) | |
print(heading1 + "\n" + heading2) | |
for currentspeed in speeds: | |
iMySpeed = int(float(currentspeed)) * iMultiplier | |
fHashtime = iDifficulty / iMySpeed | |
printTimeTable(currentspeed, sHashlabel, fHashtime, 0.0000, 0.00) | |
sys.exit() | |
#not difficulty? do nethashrate , block time | |
while True: | |
try: | |
iNethash = int(input(" Enter Network Hashrate (Example: 225534691668477): ")) | |
except (ValueError, NameError): | |
print("Not a number. Try again.") | |
continue | |
else: | |
break | |
if iNethash <= 10000: | |
print("The hashrate entered is too low to calculate.") | |
sys.exit() | |
while True: | |
try: | |
fBlocktime = float(input(" Enter current Blocktime (Example: '15.0'): ")) | |
except (ValueError, NameError): | |
print("Not a number and/or decimal. Try again.") | |
continue | |
else: | |
break | |
while True: | |
try: | |
fBlockreward = float(input(" Enter block reward or <Enter> for None (Example: '5.0'): ")) | |
except (ValueError, NameError): | |
fBlockreward = "n/a" | |
continue | |
else: | |
break | |
# ask if user wants table output or specific speed? (enter does table) | |
fOutputspeed = 0.0 | |
fOutputspeed = getuserspeed() | |
# ask user for type of output label/multiplier | |
sHashlabel = "" | |
while not sHashlabel or sHashlabel not in ('h', 'k', 'm', 'g', 't'): | |
sHashlabel = str(raw_input(" Enter output speed ('h' = Hashes', 'k' = KH, 'm' = MH, 'g' = GH, 't' = TH): ")) | |
sHashlabel = sHashlabel.upper() | |
sHashlabel, iMultiplier = setlabelmodifier(sHashlabel) | |
print(heading1 + "\n" + heading2) | |
unitsperday = 0.00 | |
for currentspeed in speeds: | |
iMySpeed = int(float(currentspeed)) * iMultiplier | |
fHashtime = iNethash / iMySpeed * fBlocktime | |
idifficulty = fHashtime * iMySpeed | |
if (fBlockreward != "n/a" or fBlockreward != ""): | |
unitsperday = (( iMySpeed * fBlockreward) / idifficulty) * 3600 * 24 | |
else: | |
unitsperday = fBlockreward | |
if fHashtime < fBlocktime: | |
fHashtime = fBlocktime * 1.1 | |
printTimeTable(currentspeed, sHashlabel, fHashtime, unitsperday, 0.00) | |
sys.exit() | |
# Nothing on command line?: | |
if not (args.symbol or args.name): | |
print("Nothing to do. Try \'-h\' - exiting.") | |
sys.exit() | |
# Lookup symbol if used and update var coinName | |
if args.symbol: | |
sSymbol = args.symbol.upper() | |
url = minerapi | |
resp = requests.get(url) | |
fulldata = json.loads(resp.text) | |
reverse_dict = {} | |
for keypath, value in keypaths(fulldata): | |
reverse_dict.setdefault(value, []).append(keypath) | |
CoinKeysLookup = reverse_dict[sSymbol] | |
# set coin name to use it for NAME LOOKUP below | |
coinName = CoinKeysLookup[0][1] | |
# NAME LOOKUP -> LOOK FOR COIN BY FULL NAME (use -n/--name) | |
try: | |
coinName # chk if defined by the symbol look already? ^ | |
except NameError: # if it isn't set the lookup to name from command line: | |
coinName = args.name # we already verified args.name exists above or wld have exited | |
# fulldata didnt populate since its a name not symbol | |
url = minerapi | |
resp = requests.get(url) | |
fulldata = json.loads(resp.text) | |
# Give user some info if found or exit. | |
for items in fulldata['coins']: | |
if items == coinName: | |
coin_id = fulldata['coins'][coinName]['id'] | |
coin_symbol = fulldata['coins'][coinName]['tag'] | |
print("%s" % setcolor(SETGREEN, "\n" + coinName + "(" + coin_symbol + ") has been found at \'whattomine.com\' with ID " + str(coin_id) + ".\n")) | |
url2 = 'https://whattomine.com/coins/' + str(coin_id) + '.json' | |
resp2 = requests.get(url2) | |
coindata = json.loads(resp2.text) | |
if 'errors' in coindata: | |
print("Sorry: The coin was found, but WhatToMine reports an error on that ID's JSON request.\n") | |
print("Try consulting the json request here: \n" + url2 + "\n") | |
sys.exit() | |
break | |
else: | |
print("\nSorry: Either your coin wasn't found or whattomine is down.\n") | |
sys.exit() | |
#sys.exit() | |
# Grab some prices for conversions | |
btc_prices = priceapi | |
#turned off verify - coindesk api ssl broken (11/2018) | |
r = requests.get(btc_prices, verify=False) | |
pricedata = json.loads(r.text) | |
USD = pricedata["bpi"]["USD"]["rate"].replace(',','') | |
GBP = pricedata["bpi"]["GBP"]["rate"].replace(',','') | |
EUR = pricedata["bpi"]["EUR"]["rate"].replace(',','') | |
BTCToUSD = float(USD) | |
BTCToGBP = float(GBP) | |
BTCToEUR = float(EUR) | |
# Grab rate/set currencies | |
fPrice = coindata.get('exchange_rate', "Not found.") | |
if not coinName == "Bitcoin": | |
fUSDrate = fPrice * BTCToUSD | |
fGBPrate = fPrice * BTCToGBP | |
fEURrate = fPrice * BTCToEUR | |
else: | |
fUSDrate = BTCToUSD | |
fGBPrate = BTCToGBP | |
fEURrate = BTCToEUR | |
fPrice = 1.00 | |
sDifficulty = coindata.get('difficulty', "Not found.") | |
fDifficulty = float(sDifficulty) | |
sCurrentblock = coindata.get('last_block', "Not found.") | |
sBlockreward = coindata.get('block_reward', "Not found.") | |
fBlockreward = float(sBlockreward) | |
sBlocktime = coindata.get('block_time', "Not found.") | |
fBlocktime = float(sBlocktime) | |
sNethash = coindata.get('nethash', "Not found.") | |
iNethash = int(str(sNethash)) | |
sAlgo = coindata.get('algorithm', "Not found.") | |
fModifier = dModifiers[sAlgo]['modifier'] | |
sHashlabel = dModifiers[sAlgo]['dHlabel'] | |
# Print results | |
print("Coin: %s" % setcolor(SETYELLOW, coinName)) | |
print( "Symbol/Tag: %s" % setcolor(SETYELLOW, coin_symbol)) | |
print("Algorithm: %s" % sAlgo) | |
print("Difficulty: %.2f" % fDifficulty) | |
print("Current Block: %s" % sCurrentblock) | |
print("Block Reward: %s" % sBlockreward) | |
print("Blocktime: %s" % sBlocktime) | |
print("Current Nethash: %s" % sNethash) | |
print("Exchange rate (BTC): %f" % fPrice) | |
print( "USD, GBP, EUR: ${:<12.4f} £{:<12.4f} €{:<12.2f}".format(fUSDrate, fGBPrate, fEURrate)) | |
if coinName == "Bitcoin": | |
print("Block Profit(1 block): ${:<12.2f} £{:<12.2f} €{:<12.2f}".format(BTCToUSD * fBlockreward, BTCToGBP * fBlockreward, BTCToEUR * fBlockreward)) | |
else: | |
print("Block Profit(1 block): ${:<12.2f} £{:<12.2f} €{:<12.2f}".format(fUSDrate * fBlockreward, fGBPrate * fBlockreward, fEURrate * fBlockreward)) | |
print(heading0) | |
# ask if user wants table output or specific speed? (enter does table) | |
fOutputspeed = getuserspeed() | |
print(heading1 + "\n" + heading2) | |
#lyra using target modifier | |
#target = 32 | |
target = 32.8 | |
multiplier = 1 | |
# 24 * 60 * 60 : | |
secsinday=86400 | |
if fOutputspeed != 0.0: | |
fSpeed = fOutputspeed * fModifier | |
wtmhashtime = round(iNethash / fSpeed * fBlocktime) | |
print(wtmhashtime) | |
print(round(wtmhashtime)) | |
print(fBlocktime) | |
if wtmhashtime <= fBlocktime: | |
wtmhashtime = int(fBlocktime + 1.0) | |
coinsperday = (secsinday * fBlockreward) / wtmhashtime | |
usd_coins = fUSDrate * coinsperday | |
printTimeTable(fOutputspeed, sHashlabel, wtmhashtime, coinsperday, usd_coins) | |
else: | |
for currentspeed in speeds: | |
fSpeed = float(currentspeed) * fModifier | |
wtmhashtime = iNethash / fSpeed * fBlocktime | |
# #generic/ethash/all others | |
# unitsperday = (fBlockreward * (secsinday / fBlocktime) * (float(currentspeed) * 1000000) / iNethash) * multiplier | |
# if sAlgo == "Lyra2REv2": | |
# unitsperday = (fBlockreward / (fDifficulty * math.pow(2, target) / (float(currentspeed) * 1000000) / 3600 / 24)) * multiplier | |
# if sAlgo == "CryptoNightV7" or sAlgo == "CryptoNight": | |
# unitsperday = (fBlockreward * (secsinday / fBlocktime)) * (float(currentspeed) * 1000000) / (fDifficulty / 60) * multiplier | |
# ^ I have left calcs above in code (for now) for 'unitsperday' which are accurate - Instead we use the data available already from W2M and previously calculated speed/hashrate/seconds: | |
coinsperday = (secsinday * fBlockreward) / wtmhashtime | |
usd_coins = fUSDrate * coinsperday | |
printTimeTable(currentspeed, sHashlabel, wtmhashtime, coinsperday, usd_coins) | |
Changes 20JUN2021
- What2Mine and price api still working. Verified.
- Updated to work with Python 3.8+. I still don't have much error trapping done. If you see an error let me know.
- NOTE: Python 2.x will no longer work. I see no point keeping it cross compatible. If you still use Python 2 as 'python' default, install 3.8.x (Most Linux distros have it available) and set it as default Python binary, or edit first line in script to point at python3 binary.
- Added some algos - havent fine tuned them yet- should be close to correct, though:
Etchash (ETC/Ether Classic), Autolykos (ERG/Ergo), Ubqhash(Ubiq)
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@argesdaniel
Just saw this. Took a look last night.
Try now. Just fixed it for v3.8.5. One problem was the change in 'collections' > 'collections.abc' among other 3.8+changes.