130 lines
5.9 KiB
Python
130 lines
5.9 KiB
Python
#!/usr/bin/env python2.7
|
|
|
|
import urllib2
|
|
import json
|
|
import subprocess
|
|
import time
|
|
import sys
|
|
import os
|
|
import datetime
|
|
|
|
# read configuration from file
|
|
|
|
cfg=json.loads(open(sys.argv[1]).read())
|
|
|
|
currency=cfg["currency"] # what's in your wallet?
|
|
pwrcost=cfg["pwrcost"] # currency units per kWh
|
|
min_profit=cfg["min_profit"] # in BTC per day; halt miners if we're below this
|
|
payment_addr=cfg["payment_addr"]
|
|
miner_name=cfg["miner_name"]
|
|
# speed in GH/s
|
|
# power in kW, measured at the wall
|
|
# power_limit in watts per card
|
|
# gpu_oc & mem_oc as offsets (+/-) from card default
|
|
# fan as % (0-100); 0=automatic control
|
|
# {ADDR} and {MINER} in cmd will be substituted with payment_addr & miner_name
|
|
#
|
|
# power_limit, gpu_oc, mem_oc, and fan may be expressed as either an integer that
|
|
# is applied to all cards or as an array of integers that get applied to each
|
|
# card in turn. This should better accomodate heterogeneous mining rigs.
|
|
performance=cfg["performance"]
|
|
|
|
os.environ["DISPLAY"]=":0"
|
|
|
|
# do string substitutions in miner commands
|
|
for algo in performance:
|
|
performance[algo]["cmd"]=performance[algo]["cmd"].format(ADDR=payment_addr, MINER=miner_name)
|
|
|
|
# fetch data from public APIs
|
|
exchrate=float(json.loads(urllib2.urlopen("https://api.coinbase.com/v2/exchange-rates?currency=BTC").read())["data"]["rates"][currency])
|
|
rawrev=json.loads(urllib2.urlopen("https://api.nicehash.com/api?method=simplemultialgo.info").read())["result"]["simplemultialgo"]
|
|
|
|
# calculate our profitability
|
|
profit={}
|
|
log=open("current-profit", "w")
|
|
for i in range(0, len(rawrev)):
|
|
try:
|
|
profit[rawrev[i]["name"]]=float(rawrev[i]["paying"])*performance[rawrev[i]["name"]]["speed"]-24.0*performance[rawrev[i]["name"]]["power"]*pwrcost/exchrate
|
|
log.write(rawrev[i]["name"]+": "+format(profit[rawrev[i]["name"]], ".8f")+" BTC/day ("+format(exchrate*profit[rawrev[i]["name"]], ".2f")+" "+currency+"/day)\n")
|
|
except:
|
|
pass
|
|
log.close()
|
|
|
|
# find maximum
|
|
max_profit=0.0
|
|
for algo in profit:
|
|
if profit[algo]>max_profit:
|
|
max_profit_algo=algo
|
|
max_profit=profit[algo]
|
|
|
|
# exit if maximum is below minimum
|
|
if (max_profit<=min_profit):
|
|
algo_log=open("algo-log", "a")
|
|
algo_log.write(str(datetime.datetime.now())+": **NONE**\n")
|
|
algo_log.close()
|
|
for algo in performance:
|
|
subprocess.call(["pkill", "-f", "^"+performance[algo]["cmd"].replace("+", "\\+")])
|
|
sys.exit(0)
|
|
|
|
try:
|
|
subprocess.check_output(["pgrep", "-f", "^"+performance[max_profit_algo]["cmd"].replace("+", "\\+")])
|
|
current=1
|
|
except:
|
|
current=0
|
|
if (current==0):
|
|
other=0
|
|
for algo in performance:
|
|
try:
|
|
subprocess.check_output(["pgrep", "-f", "^"+performance[algo]["cmd"].replace("+", "\\+")])
|
|
other=1
|
|
except:
|
|
pass
|
|
|
|
if (current==0):
|
|
# log a change
|
|
algo_log=open("algo-log", "a")
|
|
algo_log.write(str(datetime.datetime.now())+": * ("+max_profit_algo+") "+format(max_profit_val, ".8f")+" "+format(max_profit_val*exchrate, ".2f")+"\n")
|
|
algo_log.close()
|
|
if (other==1):
|
|
# kill existing miners
|
|
for algo in performance:
|
|
subprocess.call(["pkill", "-f", "^"+performance[algo]["cmd"].replace("+", "\\+")])
|
|
time.sleep(3)
|
|
# update card settings
|
|
cards=int(subprocess.check_output("nvidia-smi --query-gpu=count --format=csv,noheader,nounits".split(" ")).split("\n")[-2])
|
|
for i in range(0, cards):
|
|
# power limit
|
|
if type(performance[max_profit_algo]["power_limit"]) is int:
|
|
subprocess.call(("sudo nvidia-smi -i "+str(i)+" -pl "+str(performance[max_profit_algo]["power_limit"])).split(" "))
|
|
else:
|
|
subprocess.call(("sudo nvidia-smi -i "+str(i)+" -pl "+str(performance[max_profit_algo]["power_limit"][i])).split(" "))
|
|
# core overclock
|
|
if type(performance[max_profit_algo]["gpu_oc"]) is int:
|
|
subprocess.call(("nvidia-settings -a [gpu:"+str(i)+"]/GPUGraphicsClockOffset[2]="+str(performance[max_profit_algo]["gpu_oc"])).split(" "))
|
|
subprocess.call(("nvidia-settings -a [gpu:"+str(i)+"]/GPUGraphicsClockOffset[3]="+str(performance[max_profit_algo]["gpu_oc"])).split(" "))
|
|
else:
|
|
subprocess.call(("nvidia-settings -a [gpu:"+str(i)+"]/GPUGraphicsClockOffset[2]="+str(performance[max_profit_algo]["gpu_oc"][i])).split(" "))
|
|
subprocess.call(("nvidia-settings -a [gpu:"+str(i)+"]/GPUGraphicsClockOffset[3]="+str(performance[max_profit_algo]["gpu_oc"][i])).split(" "))
|
|
# memory overclock
|
|
if type(performance[max_profit_algo]["mem_oc"]) is int:
|
|
subprocess.call(("nvidia-settings -a [gpu:"+str(i)+"]/GPUMemoryTransferRateOffset[2]="+str(performance[max_profit_algo]["mem_oc"])).split(" "))
|
|
subprocess.call(("nvidia-settings -a [gpu:"+str(i)+"]/GPUMemoryTransferRateOffset[3]="+str(performance[max_profit_algo]["mem_oc"])).split(" "))
|
|
else:
|
|
subprocess.call(("nvidia-settings -a [gpu:"+str(i)+"]/GPUMemoryTransferRateOffset[2]="+str(performance[max_profit_algo]["mem_oc"][i])).split(" "))
|
|
subprocess.call(("nvidia-settings -a [gpu:"+str(i)+"]/GPUMemoryTransferRateOffset[3]="+str(performance[max_profit_algo]["mem_oc"][i])).split(" "))
|
|
# fan speed
|
|
if type(performance[max_profit_algo]["fan"]) is int:
|
|
if (performance[max_profit_algo]["fan"]==0):
|
|
subprocess.call(("nvidia-settings -a [gpu:"+str(i)+"]/GPUFanControlState=0").split(" "))
|
|
else:
|
|
subprocess.call(("nvidia-settings -a [gpu:"+str(i)+"]/GPUFanControlState=1").split(" "))
|
|
subprocess.call(("nvidia-settings -a [fan:"+str(i)+"]/GPUTargetFanSpeed="+str(performance[max_profit_algo]["fan"])).split(" "))
|
|
else:
|
|
if (performance[max_profit_algo]["fan"][i]==0):
|
|
subprocess.call(("nvidia-settings -a [gpu:"+str(i)+"]/GPUFanControlState=0").split(" "))
|
|
else:
|
|
subprocess.call(("nvidia-settings -a [gpu:"+str(i)+"]/GPUFanControlState=1").split(" "))
|
|
subprocess.call(("nvidia-settings -a [fan:"+str(i)+"]/GPUTargetFanSpeed="+str(performance[max_profit_algo]["fan"][i])).split(" "))
|
|
# launch new miner
|
|
subprocess.call(("screen -dmS miner "+performance[max_profit_algo]["cmd"]).split(" "))
|