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(" "))
 |