#!/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(" "))