Skip to content

Instantly share code, notes, and snippets.

@bmatthewshea
Last active January 11, 2024 05:55
Show Gist options
  • Star 9 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save bmatthewshea/90b120722e0561dd235adcdc231b6765 to your computer and use it in GitHub Desktop.
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)
#!/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:
print
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
print(heading1 + "\n" + heading2)
for currentspeed in speeds:
iMySpeed = int(float(currentspeed)) * iMultiplier
fHashtime = iDifficulty / iMySpeed
printTimeTable(currentspeed, sHashlabel, fHashtime, 0.0000, 0.00)
print
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
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)
print
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
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
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
print(heading0)
print
# 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)
print
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)
print
@bmatthewshea
Copy link
Author

bmatthewshea commented Apr 7, 2018

  • Added timetravel and xevan algos
  • Added coins per day / revenue per day in USD

whattomine-python-timetravel-added-2018-04-07_085610

@argesdaniel
Copy link

argesdaniel commented May 15, 2021

Thks for the code.
I just downloaded and compiled. I'm using Python 3.8.5.
I'm getting a syntax error on line 99
display_time(int(hashtime)), coinsperday, revenue)
^
What can it be?

@bmatthewshea
Copy link
Author

bmatthewshea commented Jun 20, 2021

@argesdaniel

Thks for the code.
I just downloaded and compiled. I'm using Python 3.8.5.
I'm getting a syntax error on line 99
display_time(int(hashtime)), coinsperday, revenue)
^
What can it be?

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.

@bmatthewshea
Copy link
Author

bmatthewshea commented Jun 20, 2021

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)

SHEA22-2021-06-20_102041

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment