# coding=utf-8 import json import os import sys import ujson import time import requests from loguru import logger from datetime import datetime, timedelta from easybitcoinrpc import RPC import csv import sqlite3 from bitcoinutils.script import Script from bitcoinutils.keys import P2wpkhAddress, P2wshAddress, P2shAddress, PrivateKey, PublicKey, SegwitAddress, P2pkhAddress from bitcoinutils.setup import setup import pymysql import pymongo from urllib import parse import btc_historical_price class UtxosIf: def __init__(self): self.balance_0 = 0 self.balance_001 = 0 self.balance_01 = 0 self.balance_1 = 0 self.balance_10 = 0 self.balance_100 = 0 self.balance_1000 = 0 self.balance_10000 = 0 self.balance_amount_0 = 0 self.balance_amount_001 = 0 self.balance_amount_01 = 0 self.balance_amount_1 = 0 self.balance_amount_10 = 0 self.balance_amount_100 = 0 self.balance_amount_1000 = 0 self.balance_amount_10000 = 0 self.profit_addresses = 0 self.loss_addresses = 0 self.profit_ratio = 0 self.lth_supply = 0 self.sth_supply = 0 self.realized_price = 0 self.relative_lth_sth = 0 self.lth_profit_supply = 0 self.lth_loss_supply = 0 self.lth_profit_ratio = 0 self.sth_profit_supply = 0 self.sth_loss_supply = 0 self.sth_profit_ratio = 0 self.slrv_ratio = 0 self.slrv_24h = 0 self.slrv_6m1y = 0 self.total_address = 0 self.miner_address = 0 self.miner_balance = 0 self.total_balance = 0 self.total_rcap = 0 self.holder_0 = 0 self.holder_1 = 0 self.holder_2 = 0 self.holder_3 = 0 self.holder_4 = 0 self.holder_5 = 0 self.holder_6 = 0 self.holder_7 = 0 self.holder_15 = 0 self.holder_30 = 0 self.holder_60 = 0 self.holder_90 = 0 self.holder_180 = 0 self.holder_360 = 0 self.holder_540 = 0 self.holder_720 = 0 self.holder_1080 = 0 self.holder_1440 = 0 self.holder_1800 = 0 self.holder_2160 = 0 self.holder_2520 = 0 self.holder_2880 = 0 self.holder_3240 = 0 self.holder_3600 = 0 self.holder_3960 = 0 self.holder_balance_0 = 0 self.holder_balance_1 = 0 self.holder_balance_2 = 0 self.holder_balance_3 = 0 self.holder_balance_4 = 0 self.holder_balance_5 = 0 self.holder_balance_6 = 0 self.holder_balance_7 = 0 self.holder_balance_15 = 0 self.holder_balance_30 = 0 self.holder_balance_60 = 0 self.holder_balance_90 = 0 self.holder_balance_180 = 0 self.holder_balance_360 = 0 self.holder_balance_540 = 0 self.holder_balance_720 = 0 self.holder_balance_1080 = 0 self.holder_balance_1440 = 0 self.holder_balance_1800 = 0 self.holder_balance_2160 = 0 self.holder_balance_2520 = 0 self.holder_balance_2880 = 0 self.holder_balance_3240 = 0 self.holder_balance_3600 = 0 self.holder_balance_3960 = 0 self.price_buy = {} # step 500 self.price_buy_amount = {} # step 500 self.diff_sell = {} # step 500 self.diff_sell_amount = {} self.profit_sell = {} # step 5000 self.profit_sell_amount = {} self.balance_0_days = {} self.balance_001_days = {} self.balance_01_days = {} self.balance_1_days = {} self.balance_10_days = {} self.balance_100_days = {} self.balance_1000_days = {} self.balance_10000_days = {} self.balance_amount_0_days = {} self.balance_amount_001_days = {} self.balance_amount_01_days = {} self.balance_amount_1_days = {} self.balance_amount_10_days = {} self.balance_amount_100_days = {} self.balance_amount_1000_days = {} self.balance_amount_10000_days = {} ''' self.current_dt = time.time() self.current_dt2 = time.gmtime(int(self.current_dt)) self.current_daystr = time.strftime("%d %b %Y", self.current_dt2) self.current_dayutc = int(time.mktime(time.strptime(self.current_daystr, "%d %b %Y"))) ''' self.mc = pymongo.MongoClient("mongodb://10.168.3.192:27018/") self.mdb = self.mc["btcutxos2"] # self.mdb.authenticate("root", "123456") self.uprofit = 0 self.uloss = 0 self.lth_nupl = 0 self.sth_nupl = 0 self.lth_mv = 0 self.sth_mv = 0 self.lth_rcap = 0 self.sth_rcap = 0 self.lth_mvrv = 0 self.sth_mvrv = 0 # 此“UtxosIf”类使用默认值初始化大量属性。以下是属性的摘要: # # - 与平衡相关的属性: # - 'balance_0', 'balance_001', ..., 'balance_10000':这些是不同面额比特币的余额计数器。 # - 'balance_amount_0', 'balance_amount_001', ..., 'balance_amount_10000':这些是与余额计数器相对应的余额金额。 # # - 与地址和利润相关的属性: # - 'profit_addresses':利润计数器地址。 # - 'loss_addresses': 丢失地址的计数器。 # - 与利润率、供应、已实现价格等相关的各种其他属性 # # # - 与持有人及其余额相关的属性: # - 不同时间间隔的持有者计数器('holder_0'、'holder_1'、...、'holder_3960')。 # - 每个持有人的相应余额属性。 # # - 与价格和交易相关的属性: # - 用于存储价格和交易金额的字典('price_buy'、'price_buy_amount'、'diff_sell'、'diff_sell_amount'、'profit_sell'、'profit_sell_amount')。 # # - 与余额随时间变化相关的属性: # - 用于存储不同时间间隔的余额变化的字典('balance_0_days'、'balance_001_days'、...、'balance_amount_10000_days')。 # # - 与 # MongoDB # 连接和其他指标相关的属性: # - 连接到MongoDB数据库('mc','mdb')。 # - 与利润、亏损、已实现资本化等相关的指标 # # 此类似乎用于管理与比特币UTXO(未花费的交易输出)相关的各种指标和数据 def init_db(self): return pymysql.connect(host="172.17.0.1", port=4419, user="root", password="IeQcJNnagkaFP1Or", database="btcdb", cursorclass=pymysql.cursors.DictCursor) # 'init_dbpymysql库来建立连接。 # # 以下是该方法的作用的细分 # # 它使用提供的主机、端口、用户名、密码和数据库名称建立与 # MySQL # 数据库的连接。 def get_vout_addr(self, rpc, txid, vout): addr = None addrtype = None ip = "127.0.0.1" port = " 8332" user = "user" password = "password" if rpc is None: rpc = RPC(ip, port, user, password) tx = None while True: try: tx = rpc.transactions.get_raw_transaction(txid, True) #break except: time.sleep(1) #print("reconnect") rpc = RPC(ip, port, user, password) continue txouts = tx["vout"] txout = None for outone in txouts: # print(outone, vout) if outone["n"] == vout: txout = outone break scriptPubKey = txout["scriptPubKey"] addrtype = scriptPubKey["type"] #print("get_vout_addr", txid, vout, tx, scriptPubKey, addrtype) if "address" not in scriptPubKey: addr = scriptPubKey["hex"] if scriptPubKey["type"] == "pubkey": temphex = scriptPubKey["hex"] try: if temphex[2:4] == "04": addr = PublicKey(temphex[2:132]).get_address(False).to_string() else: addr = PublicKey(temphex[2:68]).get_address().to_string() print("pubkey", txid, temphex, addr) except Exception as e: print("pubkey exception", txid, vout, temphex, addr, e) else: print(scriptPubKey) else: addr = scriptPubKey["address"] # print(addr) break return rpc, addr, addrtype # “UtxosIf”类中的“get_vout_addr”方法旨在检索与给定事务 (txid) 中的特定事务输出 (vout) 相对应的地址和地址类型。以下是其功能的细分: # # -参数: # - 'rpc':用于与比特币网络交互的 RPC 类的实例。 # - 'txid':交易 ID。 # - 'vout':事务输出的索引。 # # -初始化: # - 它初始化用于 RPC 连接的 IP 地址、端口、用户名和密码的变量。 # # - RPC连接: # - 如果 'rpc' 参数为 'None',则使用默认 IP、端口、用户名和密码初始化 RPC 连接。 # # - 交易检索: # - 它尝试使用 'get_raw_transaction' 方法从 RPC 对象中检索原始事务详细信息。 # - 如果在此过程中出现异常(可能是由于连接问题),它会等待 1 秒钟并重试。 # # - 提取地址: # - 一旦获得交易详细信息,它就会遍历交易的输出('vout')以找到特定的输出。 # - 它从输出中提取“scriptPubKey”及其类型。 # - 如果“scriptPubKey”不直接包含地址,它会尝试解析它(例如,如果它是一个公钥脚本)。 # # - 返回结果: # - 最后,它返回 RPC 实例、地址和地址类型。 # # 方法如下: # # '''蟒蛇 # def get_vout_addr(self、rpc、txid、vout): # addr = 无 # addrtype = 无 # ip = “127.0.0.1” # 端口 =“8332” # user = “用户” # password = “密码” # 如果 rpc 为 None: # rpc = RPC(ip, port, user, password) # tx = 无 # 而 True: # 尝试: # tx = rpc.transactions.get_raw_transaction(txid, 真) # 除了: # 时间睡眠(1) # rpc = RPC(ip, port, user, password) # 继续 # # txouts = tx[“vout”] # txout = 无 # 对于 TXOUTS 中的 Outone: # if outone[“n”] == vout: # txout = outone # 破 # scriptPubKey = txout[“scriptPubKey”] # addrtype = scriptPubKey[“类型”] # # 如果 “address” 不在 scriptPubKey 中: # addr = scriptPubKey[“十六进制”] # if scriptPubKey[“type”] == “pubkey”: # temphex = scriptPubKey[“十六进制”] # 尝试: # 如果 temphex[2:4] == “04”: # addr = PublicKey(temphex[2:132]).get_address(False).to_string() # 还: # addr = PublicKey(temphex[2:68]).get_address().to_string() # print(“pubkey”, txid, temphex, addr) # 除了 Exception as e: # print(“pubkey exception”, txid, vout, temphex, addr, e) # 还: # print(scriptPubKey) # 还: # addr = scriptPubKey[“地址”] # # 破 # # 返回 RPC、ADDR、ADdrType # ''' # # 此方法可确保从事务输出中正确提取地址和地址类型,从而处理检索过程中的潜在错误 def summary_toplist(self, txid, vout, coinbase, value, height, scriptpubkey, dt, price, dt2): if value >= 100: rpc = None rpc, addr, addrtype = self.get_vout_addr(rpc, txid, vout) if addrtype is None: addrtype = "unknown" if addr is None: addr = "unknown" toplist = {} toplist["txid"] = txid toplist["vout"] = vout toplist["coinbase"] = coinbase toplist["value"] = value toplist["height"] = height toplist["scriptpubkey"] = scriptpubkey toplist["dt"] = dt toplist["price"] = price toplist["dt2"] = dt2 toplist["addr"] = addr toplist["type"] = addrtype result = self.mdbc_toplist.find_one(toplist) if result is None or len(result) > 0: self.mdbc_toplist.insert_one(toplist) #print(self.mdbc_toplist.find_one()) # 该类中的方法似乎用于汇总和存储有关满足特定条件的事务的信息。以下是该方法的作用:summary_toplistUtxosIf # # 它需要与交易相关的几个参数(、、、)。txidvoutcoinbasevalueheightscriptpubkeydtpricedt2 # 它首先检查事务输出 () 的值是否大于或等于100。value # 如果该值满足条件,则将变量初始化为None。rpc # 然后,它调用该方法来检索与事务输出对应的地址和地址类型。get_vout_addr # 它创建一个名为的字典,其中包含有关交易的各种信息,包括其ID、输出索引、是否是 # coinbase交易、其值、区块高度、scriptPubKey、日期时间信息、价格、地址和地址类型。toplist # 它检查MongoDB集合中是否存在具有相同事务ID和输出索引的记录。如果没有,它会将字典插入到集合中。mdbc_toplisttoplist def summary_balance_days(self, dt, dt2, value, days_out, balance_out): daysecs = 3600 * 24 days = (dt - dt2)/daysecs if days < 1: if "day_0" in days_out: days_out["day_0"]+=1 balance_out["day_0"] += value else: days_out["day_0"]=1 balance_out["day_0"] = value elif days < 1 * 2: if "day_1" in days_out: days_out["day_1"] += 1 balance_out["day_1"] += value else: days_out["day_1"] = 1 balance_out["day_1"] = value elif days < 1 * 3: if "day_2" in days_out: days_out["day_2"] += 1 balance_out["day_2"] += value else: days_out["day_2"] = 1 balance_out["day_2"] = value elif days < 1 * 4: if "day_3" in days_out: days_out["day_3"] += 1 balance_out["day_3"] += value else: days_out["day_3"] = 1 balance_out["day_3"] = value elif days < 1 * 5: if "day_4" in days_out: days_out["day_4"] += 1 balance_out["day_4"] += value else: days_out["day_4"] = 1 balance_out["day_4"] = value elif days < 1 * 6: if "day_5" in days_out: days_out["day_5"] += 1 balance_out["day_5"] += value else: days_out["day_5"] = 1 balance_out["day_5"] = value elif days < 1 * 7: if "day_6" in days_out: days_out["day_6"] += 1 balance_out["day_6"] += value else: days_out["day_6"] = 1 balance_out["day_6"] = value elif days < 1 * 8: if "day_7" in days_out: days_out["day_7"] += 1 balance_out["day_7"] += value else: days_out["day_7"] = 1 balance_out["day_7"] = value elif days < 1 * 9: if "day_8" in days_out: days_out["day_8"] += 1 balance_out["day_8"] += value else: days_out["day_8"] = 1 balance_out["day_8"] = value elif days < 1 * 10: if "day_9" in days_out: days_out["day_9"] += 1 balance_out["day_9"] += value else: days_out["day_9"] = 1 balance_out["day_9"] = value elif days < 1 * 11: if "day_10" in days_out: days_out["day_10"] += 1 balance_out["day_10"] += value else: days_out["day_10"] = 1 balance_out["day_10"] = value elif days < 1 * 12: if "day_11" in days_out: days_out["day_11"] += 1 balance_out["day_11"] += value else: days_out["day_11"] = 1 balance_out["day_11"] = value elif days < 1 * 13: if "day_12" in days_out: days_out["day_12"] += 1 balance_out["day_12"] += value else: days_out["day_12"] = 1 balance_out["day_12"] = value elif days < 1 * 14: if "day_13" in days_out: days_out["day_13"] += 1 balance_out["day_13"] += value else: days_out["day_13"] = 1 balance_out["day_13"] = value elif days < 1 * 15: if "day_14" in days_out: days_out["day_14"] += 1 balance_out["day_14"] += value else: days_out["day_14"] = 1 balance_out["day_14"] = value elif days < 1 * 16: if "day_15" in days_out: days_out["day_15"] += 1 balance_out["day_15"] += value else: days_out["day_15"] = 1 balance_out["day_15"] = value elif days < 1 * 17: if "day_16" in days_out: days_out["day_16"] += 1 balance_out["day_16"] += value else: days_out["day_16"] = 1 balance_out["day_16"] = value elif days < 1 * 18: if "day_17" in days_out: days_out["day_17"] += 1 balance_out["day_17"] += value else: days_out["day_17"] = 1 balance_out["day_17"] = value elif days < 1 * 19: if "day_18" in days_out: days_out["day_18"] += 1 balance_out["day_18"] += value else: days_out["day_18"] = 1 balance_out["day_18"] = value elif days < 1 * 20: if "day_19" in days_out: days_out["day_19"] += 1 balance_out["day_19"] += value else: days_out["day_19"] = 1 balance_out["day_19"] = value elif days < 1 * 21: if "day_20" in days_out: days_out["day_20"] += 1 balance_out["day_20"] += value else: days_out["day_20"] = 1 balance_out["day_20"] = value elif days < 1 * 22: if "day_21" in days_out: days_out["day_21"] += 1 balance_out["day_21"] += value else: days_out["day_21"] = 1 balance_out["day_21"] = value elif days < 1 * 23: if "day_22" in days_out: days_out["day_22"] += 1 balance_out["day_22"] += value else: days_out["day_22"] = 1 balance_out["day_22"] = value elif days < 1 * 24: if "day_23" in days_out: days_out["day_23"] += 1 balance_out["day_23"] += value else: days_out["day_23"] = 1 balance_out["day_23"] = value elif days < 1 * 25: if "day_24" in days_out: days_out["day_24"] += 1 balance_out["day_24"] += value else: days_out["day_24"] = 1 balance_out["day_24"] = value elif days < 1 * 26: if "day_25" in days_out: days_out["day_25"] += 1 balance_out["day_25"] += value else: days_out["day_25"] = 1 balance_out["day_25"] = value elif days < 1 * 27: if "day_26" in days_out: days_out["day_26"] += 1 balance_out["day_26"] += value else: days_out["day_26"] = 1 balance_out["day_26"] = value elif days < 1 * 28: if "day_27" in days_out: days_out["day_27"] += 1 balance_out["day_27"] += value else: days_out["day_27"] = 1 balance_out["day_27"] = value elif days < 1 * 29: if "day_28" in days_out: days_out["day_28"] += 1 balance_out["day_28"] += value else: days_out["day_28"] = 1 balance_out["day_28"] = value elif days < 1 * 30: if "day_29" in days_out: days_out["day_29"] += 1 balance_out["day_29"] += value else: days_out["day_29"] = 1 balance_out["day_29"] = value elif days < 1 * 31: if "day_30" in days_out: days_out["day_30"] += 1 balance_out["day_30"] += value else: days_out["day_30"] = 1 balance_out["day_30"] = value elif days < 1 * 60: if "day_60" in days_out: days_out["day_60"] += 1 balance_out["day_60"] += value else: days_out["day_60"] = 1 balance_out["day_60"] = value elif days < 1 * 90: if "day_90" in days_out: days_out["day_90"] += 1 balance_out["day_90"] += value else: days_out["day_90"] = 1 balance_out["day_90"] = value elif days < 1 * 180: if "day_180" in days_out: days_out["day_180"] += 1 balance_out["day_180"] += value else: days_out["day_180"] = 1 balance_out["day_180"] = value elif days < 1 * 360: if "day_360" in days_out: days_out["day_360"] += 1 balance_out["day_360"] += value else: days_out["day_360"] = 1 balance_out["day_360"] = value elif days < 1 * 540: if "day_540" in days_out: days_out["day_540"] += 1 balance_out["day_540"] += value else: days_out["day_540"] = 1 balance_out["day_540"] = value elif days < 1 * 720: if "day_720" in days_out: days_out["day_720"] += 1 balance_out["day_720"] += value else: days_out["day_720"] = 1 balance_out["day_720"] = value elif days < 1 * 1080: if "day_1080" in days_out: days_out["day_1080"] += 1 balance_out["day_1080"] += value else: days_out["day_1080"] = 1 balance_out["day_1080"] = value elif days < 1 * 1440: if "day_1440" in days_out: days_out["day_1440"] += 1 balance_out["day_1440"] += value else: days_out["day_1440"] = 1 balance_out["day_1440"] = value elif days < 1 * 1880: if "day_1880" in days_out: days_out["day_1880"] += 1 balance_out["day_1880"] += value else: days_out["day_1880"] = 1 balance_out["day_1880"] = value elif days < 1 * 2160: if "day_2160" in days_out: days_out["day_2160"] += 1 balance_out["day_2160"] += value else: days_out["day_2160"] = 1 balance_out["day_2160"] = value elif days < 1 * 2520: if "day_2520" in days_out: days_out["day_2520"] += 1 balance_out["day_2520"] += value else: days_out["day_2520"] = 1 balance_out["day_2520"] = value elif days < 1 * 2880: if "day_2880" in days_out: days_out["day_2880"] += 1 balance_out["day_2880"] += value else: days_out["day_2880"] = 1 balance_out["day_2880"] = value elif days < 1 * 3240: if "day_3240" in days_out: days_out["day_3240"] += 1 balance_out["day_3240"] += value else: days_out["day_3240"] = 1 balance_out["day_3240"] = value elif days < 1 * 3600: if "day_3600" in days_out: days_out["day_3600"] += 1 balance_out["day_3600"] += value else: days_out["day_3600"] = 1 balance_out["day_3600"] = value else: if "day_3960" in days_out: days_out["day_3960"] += 1 balance_out["day_3960"] += value else: days_out["day_3960"] = 1 balance_out["day_3960"] = value return days_out, balance_out # “summary_balance_days”功能似乎旨在根据持有余额的天数对余额进行分类。以下是其工作原理的细分: # # -参数: # - 'dt':当前日期。 # - “dt2”:获取余额的日期。 # - 'value':余额的值。 # - 'days_out':用于存储不同时期余额计数的字典。 # - 'balance_out':存储不同时期累计余额的字典。 # # - 计算天数: # - 它通过从当前日期 ('dt') 中减去购置日期 ('dt2') 来计算余额持有的天数。此值存储在“days”变量中。 # # - 对余额进行分类: # - 根据计算的天数,该函数将余额分配给特定类别 ('day_X'),其中 # 'X' # 表示天数。 # - 如果余额持有时间少于一天,则将其归类为“day_0”。 # - 如果余额已持有 # 1 # 到 # 30 # 天,则分别归类为“day_1”至“day_30”。 # - 如果余额已持有 # 31 # 到 # 60 # 天,则将其归类为“day_60”。 # - 如果余额已持有超过 # 3600 # 天(约 # 10 # 年),则将其归类为“day_3960”。 # # - 累积余额: # - 对于每个类别,该函数递增余额计数 ('days_out') 并将余额值添加到累计余额 ('balance_out')。 # # - 返回结果: # - 返回更新的“days_out”和“balance_out”字典。 # 0.01,0.1,1,10,100,1000,10000,total balance # new addr, total address def summary_utxos(self, current_price, txid, vout, coinbase, value, height, scriptpubkey, dt, price, dt2): self.total_address += 1 self.total_balance += value self.total_rcap += (value * price) if coinbase == 1: self.miner_address += 1 self.miner_balance += value if current_price > price: self.uprofit +=(value*(current_price-price)) self.profit_addresses += 1 if current_price <= price: self.uloss += (value*(price - current_price)) self.loss_addresses += 1 n = int(price / 1000) n = n * 1000 keystr = "buy" + str(n) if keystr in self.price_buy: self.price_buy[keystr] += 1 else: self.price_buy[keystr] = 1 keystr = "buy_amount" + str(n) if keystr in self.price_buy_amount: self.price_buy_amount[keystr] += value else: self.price_buy_amount[keystr] = value diff = current_price - price n = int(diff / 1000) n = n * 1000 keystr = "diff" + str(n) if keystr in self.diff_sell: self.diff_sell[keystr] += 1 else: self.diff_sell[keystr] = 1 keystr = "diff_amount" + str(n) if keystr in self.diff_sell_amount: self.diff_sell_amount[keystr] += value else: self.diff_sell_amount[keystr] = value try: profit = (current_price - price)/(price)*10 except: profit = current_price*10 if int(profit) < 100: n = int(profit) keystr = "profit" + str(n) if keystr in self.profit_sell: self.profit_sell[keystr] += 1 else: self.profit_sell[keystr] = 1 keystr = "profit_amount" + str(n) if keystr in self.profit_sell_amount: self.profit_sell_amount[keystr] += value else: self.profit_sell_amount[keystr] = value else: profit = profit/100 n = int(profit) keystr = "profit10" + str(n) if keystr in self.profit_sell: self.profit_sell[keystr] += 1 else: self.profit_sell[keystr] = 1 keystr = "profit_amount10" + str(n) if keystr in self.profit_sell_amount: self.profit_sell_amount[keystr] += value else: self.profit_sell_amount[keystr] = value if value < 0.01: self.balance_0 += 1 self.balance_amount_0 += value self.balance_0_days, self.balance_amount_0_days = self.summary_balance_days(self.current_dayutc, dt2, value, self.balance_0_days, self.balance_amount_0_days) elif value < 0.1: self.balance_001 += 1 self.balance_amount_001 += value self.balance_001_days, self.balance_amount_001_days = self.summary_balance_days(self.current_dayutc, dt2, value, self.balance_001_days, self.balance_amount_001_days) elif value < 1: self.balance_01 += 1 self.balance_amount_01 += value self.balance_01_days, self.balance_amount_01_days = self.summary_balance_days(self.current_dayutc, dt2, value, self.balance_01_days, self.balance_amount_01_days) elif value < 10: self.balance_1 += 1 self.balance_amount_1 += value self.balance_1_days, self.balance_amount_1_days = self.summary_balance_days(self.current_dayutc, dt2, value, self.balance_1_days, self.balance_amount_1_days) elif value < 100: self.balance_10 += 1 self.balance_amount_10 += value self.balance_10_days, self.balance_amount_10_days = self.summary_balance_days(self.current_dayutc, dt2, value, self.balance_10_days, self.balance_amount_10_days) elif value < 1000: self.balance_100 += 1 self.balance_amount_100 += value self.balance_100_days, self.balance_amount_100_days = self.summary_balance_days(self.current_dayutc, dt2, value, self.balance_100_days, self.balance_amount_100_days) elif value < 10000: self.balance_1000 += 1 self.balance_amount_1000 += value self.balance_1000_days, self.balance_amount_1000_days = self.summary_balance_days(self.current_dayutc, dt2, value, self.balance_1000_days, self.balance_amount_1000_days) else: self.balance_10000 += 1 self.balance_amount_10000 += value self.balance_10000_days, self.balance_amount_10000_days = self.summary_balance_days(self.current_dayutc, dt2, value, self.balance_10000_days, self.balance_amount_10000_days) daysecs = 3600 * 24 if self.current_dayutc - dt2 >= 180 * daysecs: if self.current_dayutc - dt2 <= 365 * daysecs: self.slrv_6m1y += (value*price) if self.current_dayutc - dt2 <= daysecs: self.slrv_24h += (value*price) if self.current_dayutc - dt2 >= 155*daysecs: self.lth_nupl += (value*(current_price-price)) self.lth_rcap += (value*price) self.lth_mv += (value*current_price) self.lth_supply += value if current_price > price: self.lth_profit_supply += value if current_price < price: self.lth_loss_supply += value else: self.sth_nupl += (value*(price - current_price)) self.sth_rcap += (value * price) self.sth_mv += (value * current_price) self.sth_supply += value if current_price > price: self.sth_profit_supply += value if current_price < price: self.sth_loss_supply += value if self.current_dayutc - dt2 < daysecs: self.holder_0 += 1 self.holder_balance_0 += value elif self.current_dayutc - dt2 < daysecs * 2: self.holder_1 += 1 self.holder_balance_1 += value elif self.current_dayutc - dt2 < daysecs * 3: self.holder_2 += 1 self.holder_balance_2 += value elif self.current_dayutc - dt2 < daysecs * 4: self.holder_3 += 1 self.holder_balance_3 += value elif self.current_dayutc - dt2 < daysecs * 5: self.holder_4 += 1 self.holder_balance_4 += value elif self.current_dayutc - dt2 < daysecs * 6: self.holder_5 += 1 self.holder_balance_5 += value elif self.current_dayutc - dt2 < daysecs * 7: self.holder_6 += 1 self.holder_balance_6 += value elif self.current_dayutc - dt2 < daysecs * 8: self.holder_7 += 1 self.holder_balance_7 += value elif self.current_dayutc - dt2 < daysecs * 15: self.holder_15 += 1 self.holder_balance_15 += value elif self.current_dayutc - dt2 < daysecs * 30: self.holder_30 += 1 self.holder_balance_30 += value elif self.current_dayutc - dt2 < daysecs * 60: self.holder_60 += 1 self.holder_balance_60 += value elif self.current_dayutc - dt2 < daysecs * 90: self.holder_90 += 1 self.holder_balance_90 += value elif self.current_dayutc - dt2 < daysecs * 180: self.holder_180 += 1 self.holder_balance_180 += value elif self.current_dayutc - dt2 < daysecs * 360: self.holder_360 += 1 self.holder_balance_360 += value elif self.current_dayutc - dt2 < daysecs * 540: self.holder_540 += 1 self.holder_balance_540 += value elif self.current_dayutc - dt2 < daysecs * 720: self.holder_720 += 1 self.holder_balance_720 += value elif self.current_dayutc - dt2 < daysecs * 1080: self.holder_1080 += 1 self.holder_balance_1080 += value elif self.current_dayutc - dt2 < daysecs * 1440: self.holder_1440 += 1 self.holder_balance_1440 += value elif self.current_dayutc - dt2 < daysecs * 1880: self.holder_1800 += 1 self.holder_balance_1800 += value elif self.current_dayutc - dt2 < daysecs * 2160: self.holder_2160 += 1 self.holder_balance_2160 += value elif self.current_dayutc - dt2 < daysecs * 2520: self.holder_2520 += 1 self.holder_balance_2520 += value elif self.current_dayutc - dt2 < daysecs * 2880: self.holder_2880 += 1 self.holder_balance_2880 += value elif self.current_dayutc - dt2 < daysecs * 3240: self.holder_3240 += 1 self.holder_balance_3240 += value elif self.current_dayutc - dt2 < daysecs * 3600: self.holder_3600 += 1 self.holder_balance_3600 += value else: self.holder_3960 += 1 self.holder_balance_3960 += value # “summary_utxos”功能似乎是用于分析交易及其相关余额的更大系统的一部分。以下是该函数的功能细分: # # - ** 更新计数器和总计: ** # - 'self.total_address':递增 # 1 # 以计算地址总数。 # - “self.total_balance”:累积总余额。 # - 'self.total_rcap':累计已实现的总资本化(余额 * 价格)。 # - “self.miner_address”和“self.miner_balance”:如果交易是 # coinbase # 交易(“coinbase == 1”),则增加矿工地址计数并累积矿工余额。 # # - ** 盈亏计算: ** # - 'self.uprofit' # 和 # 'self.uloss':如果当前价格分别高于或低于成交价格,则计算并累计未实现损益。 # - “self.profit_addresses”和“self.loss_addresses”:统计有未实现盈亏的地址。 # # - ** 价格分析: ** # - 'n':将价格四舍五入到最接近的千位。 # - 'keystr':根据四舍五入的价格生成密钥。 # - 更新买入价格(“self.price_buy”)及其相应金额(“self.price_buy_amount”)的计数器。 # - 计算当前价格和交易价格之间的差额 ('diff'),将其四舍五入到最接近的千位,并更新价格差异计数器('self.diff_sell' # 和 # 'self.diff_sell_amount')。 # # - ** 利润百分比分析: ** # - 根据当前价格和交易价格之间的差额计算利润百分比(“利润”)。 # - 将利润分类为不同的范围,并相应地更新计数器(“self.profit_sell”和“self.profit_sell_amount”)。 # # - ** 余额分类: ** # - 根据余额的值将余额分类为不同的范围,并相应地更新计数器。 # - 对于每个范围,更新地址计数 ('self.balance_X') 和累计余额 ('self.balance_amount_X') 的计数器。 # # - ** 基于时间的分析: ** # - 分析余额被持有的时间(“self.summary_balance_days”)并相应地更新计数器。 # # - ** 其他指标: ** # - 根据余额持有的时间长度更新各种指标,例如短期和长期已实现资本化、市场价值、供应、利润和损失。 # # - ** 持有人分析: ** # - 根据持有余额的时间对持有人进行分类,并更新相应的计数器,以显示持有人的数量及其累计余额。 # # 总体而言,此功能似乎提供了对交易数据的全面分析,包括与余额、价格、利润和持有人行为相关的各种指标。 def save_db(self): db_conn = self.init_db() with db_conn.cursor() as cursor: sql_insert = 'REPLACE INTO `utxosv3` (`unixdt`, `total_address`, `total_balance`, `total_rcap`, `miner_address`,`miner_balance`, `balance_0`, `balance_001`, `balance_01`, `balance_1`, `balance_10`,`balance_100`, `balance_1000`, `balance_10000`, uprofit, uloss, lthnupl, sthnupl, lthmarketcap, lthrcap, sthmarketcap, sthrcap, lthmvrv, sthmvrv) VALUES (FROM_UNIXTIME(%s), %s, %s, %s, %s, %s,%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)' cursor.execute(sql_insert, ( self.current_dayutc, self.total_address, self.total_balance, self.total_rcap, self.miner_address, self.miner_balance, self.balance_0, self.balance_001, self.balance_01, self.balance_1, self.balance_10, self.balance_100, self.balance_1000, self.balance_10000, self.uprofit, self.uloss, self.lth_nupl, self.sth_nupl, self.lth_mv, self.lth_rcap, self.sth_mv, self.sth_rcap, self.lth_mvrv, self.sth_mvrv)) sql_insert = 'REPLACE INTO `utxos3nd` (`unixdt`, `balance_amount_0`, `balance_amount_001`, `balance_amount_01`, `balance_amount_1`, `balance_amount_10`,`balance_amount_100`, `balance_amount_1000`, `balance_amount_10000`) VALUES (FROM_UNIXTIME(%s), %s, %s, %s, %s, %s,%s, %s, %s)' cursor.execute(sql_insert, ( self.current_dayutc, self.balance_amount_0, self.balance_amount_001, self.balance_amount_01, self.balance_amount_1, self.balance_amount_10, self.balance_amount_100, self.balance_amount_1000, self.balance_amount_10000)) sql_insert = 'REPLACE INTO `holder3` (`unixdt`,`holder_0`,`holder_1`,`holder_2`,`holder_3`,`holder_4`,`holder_5`,`holder_6`,`holder_7`,`holder_15`,`holder_30`,`holder_60`,`holder_90`,`holder_180`,`holder_360`,`holder_540`,`holder_720`,`holder_1080`,`holder_1440`,`holder_1800`,`holder_2160`,`holder_2520`,`holder_2880`,`holder_3240`,`holder_3600`,`holder_3960`) VALUES(FROM_UNIXTIME(%s), %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)' cursor.execute(sql_insert, ( self.current_dayutc, self.holder_0, self.holder_1, self.holder_2, self.holder_3, self.holder_4, self.holder_5, self.holder_6, self.holder_7, self.holder_15, self.holder_30, self.holder_60, self.holder_90, self.holder_180, self.holder_360, self.holder_540, self.holder_720, self.holder_1080, self.holder_1440, self.holder_1800, self.holder_2160, self.holder_2520, self.holder_2880, self.holder_3240, self.holder_3600, self.holder_3960)) sql_insert = 'REPLACE INTO `holder_balance3` (`unixdt`,`holder_balance_0`,`holder_balance_1`,`holder_balance_2`,`holder_balance_3`,`holder_balance_4`,`holder_balance_5`,`holder_balance_6`,`holder_balance_7`,`holder_balance_15`,`holder_balance_30`,`holder_balance_60`,`holder_balance_90`,`holder_balance_180`,`holder_balance_360`,`holder_balance_540`,`holder_balance_720`,`holder_balance_1080`,`holder_balance_1440`,`holder_balance_1800`,`holder_balance_2160`,`holder_balance_2520`,`holder_balance_2880`,`holder_balance_3240`,`holder_balance_3600`,`holder_balance_3960`) VALUES(FROM_UNIXTIME(%s), %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)' cursor.execute(sql_insert, ( self.current_dayutc, self.holder_balance_0, self.holder_balance_1, self.holder_balance_2, self.holder_balance_3, self.holder_balance_4, self.holder_balance_5, self.holder_balance_6, self.holder_balance_7, self.holder_balance_15, self.holder_balance_30, self.holder_balance_60, self.holder_balance_90, self.holder_balance_180, self.holder_balance_360, self.holder_balance_540, self.holder_balance_720, self.holder_balance_1080, self.holder_balance_1440, self.holder_balance_1800, self.holder_balance_2160, self.holder_balance_2520, self.holder_balance_2880, self.holder_balance_3240, self.holder_balance_3600, self.holder_balance_3960)) #v2 if self.loss_addresses > 0: self.profit_ratio = self.profit_addresses/self.loss_addresses if self.total_balance > 0: self.realized_price = self.total_rcap/self.total_balance if self.sth_loss_supply > 0: self.sth_profit_ratio = self.sth_profit_supply/self.sth_loss_supply if self.lth_loss_supply > 0: self.lth_profit_ratio = self.lth_profit_supply / self.lth_loss_supply if self.sth_profit_ratio > 0: self.relative_lth_sth = self.lth_profit_ratio/self.sth_profit_ratio if self.slrv_6m1y > 0: self.slrv_ratio = self.slrv_24h/self.slrv_6m1y sql_insert = 'REPLACE INTO `utxosv4` (`unixdt`,`profit_addresses`,`loss_addresses`,`profit_ratio`,`lth_supply`,`sth_supply`,`realized_price`,`relative_lth_sth`,`lth_profit_supply`,`lth_loss_supply`,`lth_profit_ratio`,`sth_profit_supply`,`sth_loss_supply`,`sth_profit_ratio`,`slrv_ratio`) VALUES(FROM_UNIXTIME(%s), %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)' cursor.execute(sql_insert, (self.current_dayutc, self.profit_addresses, self.loss_addresses, self.profit_ratio, self.lth_supply, self.sth_supply, self.realized_price, self.relative_lth_sth, self.lth_profit_supply, self.lth_loss_supply, self.lth_profit_ratio, self.sth_profit_supply, self.sth_loss_supply, self.sth_profit_ratio, self.slrv_ratio)) db_conn.commit() mc = pymongo.MongoClient("mongodb://10.168.3.192:27018/") mdb = mc["btcutxos2"] # mdb.authenticate("root", "123456") self.price_buy["unixdt"] = int(self.current_dt) mdbc_buy = mdb["buy"] mdbc_buy.insert_one(self.price_buy) print(mdbc_buy.find_one()) self.price_buy_amount["unixdt"] = int(self.current_dt) mdbc_buy_amount = mdb["buy_amount"] mdbc_buy_amount.insert_one(self.price_buy_amount) print(mdbc_buy_amount.find_one()) self.diff_sell["unixdt"] = int(self.current_dt) mdbc_diff = mdb["diff"] mdbc_diff.insert_one(self.diff_sell) print(mdbc_diff.find_one()) self.diff_sell_amount["unixdt"] = int(self.current_dt) mdbc_diff_amount = mdb["diff_amount"] mdbc_diff_amount.insert_one(self.diff_sell_amount) print(mdbc_diff_amount.find_one()) self.profit_sell["unixdt"] = int(self.current_dt) mdbc_profit = mdb["profit"] mdbc_profit.insert_one(self.profit_sell) print(mdbc_profit.find_one()) self.profit_sell_amount["unixdt"] = int(self.current_dt) mdbc_profit_amount = mdb["profit_amount"] mdbc_profit_amount.insert_one(self.profit_sell_amount) print(mdbc_profit_amount.find_one()) self.balance_0_days["unixdt"] = int(self.current_dt) mdbc_balance_days = mdb["balance_0_days"] mdbc_balance_days.insert_one(self.balance_0_days) print(mdbc_balance_days.find_one()) self.balance_amount_0_days["unixdt"] = int(self.current_dt) mdbc_balance_amount_days = mdb["balance_amount_0_days"] mdbc_balance_amount_days.insert_one(self.balance_amount_0_days) print(mdbc_balance_amount_days.find_one()) self.balance_001_days["unixdt"] = int(self.current_dt) mdbc_balance_days = mdb["balance_001_days"] mdbc_balance_days.insert_one(self.balance_001_days) print(mdbc_balance_days.find_one()) self.balance_amount_001_days["unixdt"] = int(self.current_dt) mdbc_balance_amount_days = mdb["balance_amount_001_days"] mdbc_balance_amount_days.insert_one(self.balance_amount_001_days) print(mdbc_balance_amount_days.find_one()) self.balance_01_days["unixdt"] = int(self.current_dt) mdbc_balance_days = mdb["balance_01_days"] mdbc_balance_days.insert_one(self.balance_01_days) print(mdbc_balance_days.find_one()) self.balance_amount_01_days["unixdt"] = int(self.current_dt) mdbc_balance_amount_days = mdb["balance_amount_01_days"] mdbc_balance_amount_days.insert_one(self.balance_amount_01_days) print(mdbc_balance_amount_days.find_one()) self.balance_1_days["unixdt"] = int(self.current_dt) mdbc_balance_days = mdb["balance_1_days"] mdbc_balance_days.insert_one(self.balance_1_days) print(mdbc_balance_days.find_one()) self.balance_amount_1_days["unixdt"] = int(self.current_dt) mdbc_balance_amount_days = mdb["balance_amount_1_days"] mdbc_balance_amount_days.insert_one(self.balance_amount_1_days) print(mdbc_balance_amount_days.find_one()) self.balance_10_days["unixdt"] = int(self.current_dt) mdbc_balance_days = mdb["balance_10_days"] mdbc_balance_days.insert_one(self.balance_10_days) print(mdbc_balance_days.find_one()) self.balance_amount_10_days["unixdt"] = int(self.current_dt) mdbc_balance_amount_days = mdb["balance_amount_10_days"] mdbc_balance_amount_days.insert_one(self.balance_amount_10_days) print(mdbc_balance_amount_days.find_one()) self.balance_100_days["unixdt"] = int(self.current_dt) mdbc_balance_days = mdb["balance_100_days"] mdbc_balance_days.insert_one(self.balance_100_days) print(mdbc_balance_days.find_one()) self.balance_amount_100_days["unixdt"] = int(self.current_dt) mdbc_balance_amount_days = mdb["balance_amount_100_days"] mdbc_balance_amount_days.insert_one(self.balance_amount_100_days) print(mdbc_balance_amount_days.find_one()) self.balance_1000_days["unixdt"] = int(self.current_dt) mdbc_balance_days = mdb["balance_1000_days"] mdbc_balance_days.insert_one(self.balance_1000_days) print(mdbc_balance_days.find_one()) self.balance_amount_1000_days["unixdt"] = int(self.current_dt) mdbc_balance_amount_days = mdb["balance_amount_1000_days"] mdbc_balance_amount_days.insert_one(self.balance_amount_1000_days) print(mdbc_balance_amount_days.find_one()) self.balance_10000_days["unixdt"] = int(self.current_dt) mdbc_balance_days = mdb["balance_10000_days"] mdbc_balance_days.insert_one(self.balance_10000_days) print(mdbc_balance_days.find_one()) self.balance_amount_10000_days["unixdt"] = int(self.current_dt) mdbc_balance_amount_days = mdb["balance_amount_10000_days"] mdbc_balance_amount_days.insert_one(self.balance_amount_10000_days) print(mdbc_balance_amount_days.find_one()) # “save_db”方法负责将分析期间收集的数据保存到数据库和MongoDB实例中。以下是它的作用的细分: # - MySQL数据库: ** # - 使用'init_db'方法连接到MySQL数据库。 # - 使用SQL查询将记录插入或替换到多个表('utxosv1'、'utxos2nd'、'holder'、'holder_balance'和'utxosv2')中。这些表包含用于分析的各种指标和汇总数据。 # - ** MongoDB: ** # - 连接到MongoDB实例。 # - 将数据插入多个集合('buy'、'buy_amount'、'diff'、'diff_amount'、'profit'、'profit_amount'、'balance_0_days'、'balance_amount_0_days' # 等)中,以进行不同类型的分析。 # 对于MongoDB中的每个集合,该方法将数据插入到集合中,并打印插入的文档以进行验证。 # 此外,该方法还会计算和更新一些其他指标(如利润率、实现价格、短期和长期持有者的利润率、相对比率等),并将它们插入“utxosv2”表中。 # 总体而言,这种方法有助于将分析的数据存储到关系数据库 (MySQL) 和NoSQL数据库 (MongoDB) 中,从而便于检索和进一步分析。 def get_history_price(self): prices = {} response_price = requests.get( 'https://data.nasdaq.com/api/v3/datatables/QDL/BCHAIN?code=MKPRU;api_key=FZqXog4sR-b7cYnXcRVV') if response_price.status_code == 200: #print(response_price.content) priceweb = ujson.loads(response_price.content) if "datatable" in priceweb: priceset = priceweb["datatable"] if "data" in priceset: pricedata = priceset["data"] for price in pricedata: daystr = price[1] p = price[2] dayutc = time.mktime(time.strptime(daystr, "%Y-%m-%d")) prices[str(int(dayutc))] = float(p) #print(price, int(dayutc), g_prices[str(int(dayutc))]) return prices # “get_history_price”方法似乎用于从WebAPI终结点检索历史价格数据。以下是它的作用的细分: # - 初始化一个空字典“prices”来存储历史价格数据。 # - 向指定的API端点发送HTTP GET请求,该端点可能提供历史比特币价格数据。 # - 检查响应状态代码是否为200(表示响应成功)。 # - 如果响应成功: # - 使用“ujson”库解析JSON响应。 # - 检查解析的JSON响应中是否存在键'“dataset''。 # - 如果'“dataset”'存在: # - 检索“data”' 字段,该字段可能包含历史价格数据点的列表。 # - 遍历列表中的每个价格数据点。 # - 使用'strptime'将日期字符串 ('daystr') 解析为Unix时间戳 ('dayutc')。 # - 将Unix 时间戳作为键存储在“prices”字典中,并将相应的price ('p') 作为值。 # 最后,它返回包含历史价格数据的“prices”字典,其中Unix时间戳作为键,价格作为值 def get_history_price2(self, pricedict): #pricedict = {} dayt = time.gmtime() daystr = time.strftime("%Y", dayt) year = int(daystr) end_year = year while True: url = "" if end_year != year: start_year = end_year url = "https://data.messari.io/api/v1/assets/bitcoin/metrics/price/time-series?start=" else: url = "https://data.messari.io/api/v1/assets/bitcoin/metrics/price/time-series?after=" + str( year) + "-01-01&order=descending&interval=1d" if end_year != year: url = url + str(start_year) + "-01-01&end=" + str(end_year) + "-12-31&order=descending&interval=1d" header_set = {} header_set["x-messari-api-key"] = "aH2pyj5i4QGo1k1gLxXEbIJ5RJr+FYKLEWk6cRT6RuSc6lRY" # header_set["Content-Type"] = "application/json" print(header_set, url) response_price = requests.get(url, headers=header_set) # print(response_price) if response_price.status_code == 200: # print(response_price.content) priceweb = ujson.loads(response_price.content) if "data" in priceweb: priceset = priceweb["data"] if "values" in priceset: valueset = priceset["values"] if valueset is not None: for supply in valueset: dayutc = int(supply[0] / 1000) s = supply[1] ret_time = time.gmtime(dayutc) ret_daystr = time.strftime("%d %b %Y", ret_time) ret_dayutc = int(time.mktime(time.strptime(ret_daystr, "%d %b %Y"))) pricedict[str(ret_dayutc)] = float(s) # print(s, dayutc, pricedict[str(dayutc)]) # break else: break else: break end_year -= 1 time.sleep(2) print(pricedict) return pricedict # 'get_history # 初始化与时间相关的变量,包括当前年份。 # 进入一个循环,从当前年份开始,向后循环访问年份。 # 根据当前年份和端点构造一个URL,用于获取比特币价格数据。 # 向构造的URL发送HTTP # GET请求,包括标头中的特定API密钥。 # 检查响应状态代码是否为 # 2 # 使用'u 解析 JSON 响应 # 从JSON响应中提取相关价格数据,并将其添加到“pricedict” # 将时间戳转换为人类可读的日期字符串,然后转换回Unix时间戳,并将它们作为键存储在'pricedic 中 # 在发出下一个请求之前暂停执行2秒,以避免API过载。 # 继续此过程,直到遍历所有年份或发生错误。 # 最后,它返回“pricedict” def get_current_price(self): price = 0 try: response_price = requests.get( 'https://api.binance.com/api/v3/ticker/price?symbol=BTCUSDT') prices = ujson.loads(response_price.text) price = float(prices["price"]) print(response_price.text, price) response_price.close() # print("price", price) return price except: response_price = requests.get("https://api.coinpaprika.com/v1/tickers/btc-bitcoin") prices = ujson.loads(response_price.text) price = float(prices["quotes"]["USD"]["price"]) response_price.close() return price # 该函数似乎是一种从特定 # API端点检索比特币当前价格的方法。以下是其功能的细分:get_current_price # 初始化默认值为0的变量。price # 向指定的URL () 发送HTTP # GET请求,以获取当前以美元为单位的比特币价格。'https://bitcoinexplorer.org/api/price/usd' # 检查响应状态代码是否为200,表示响应成功。 # 如果响应成功,它将打印响应文本(大概是当前价格),并在删除逗号并将其转换为浮点数后将其分配给变量。price # 关闭响应对象。 # 打印检索到的价格以进行调试。 # 返回检索到的价格。 # 此函数实质上是从指定的API端点检索和处理当前比特币价格,并将其作为浮点数返回 def get_ht(self): try: with open('height_time.csv', mode='r') as f: reader = csv.reader(f) height_time = {rows[0]: rows[1] for rows in reader} '''for key in height_time.keys(): print(key, height_time[key]) break''' return height_time return None except: return None # 这get_ht函数似乎从名为“height_time.csv” # 的CSV文件中读取数据并将其转换为字典,其中键取自第一列,值取自第二列。以下是其功能的细分: # 尝试在读取模式下打开文件“height_time.csv” # 初始化名为“height”的字典 # 使用“csv.reade”循环访问CSV文件中的每一行 # 构造一个字典,其中每个键值对对应于CSV文件中的一行,键取自第一列,值取自第二列。 # 返回“height_time # 如果在文件读取或字典构造过程中发生异常,该函数将返回“无” # 此函数实质上是从CSV文件中读取数据并将其作为字典返回,该字典可能在代码中的其他位置用于进一步处理或分析 def get_day_utc(self, utc_time): t = time.gmtime(utc_time) daystr = time.strftime("%d %b %Y", t) dayutc = int(time.mktime(time.strptime(daystr, "%d %b %Y"))) return dayutc # 该函数将 # UTC时间戳作为输入,并返回表示与该输入时间戳对应的一天 (00:00:00UTC) 开始的 # UTC时间戳。以下是其功能的细分:get_day_utc # 它接收UTC时间戳作为输入。utc_time # 它使用将UTC时间戳转换为时间元组。此元组表示UTC中的时间。time.gmtime() # 它将时间元组格式化为一个字符串,以“DD Mon YYYY” # (例如,“2022年1月1日”)表示日期。 # 它使用和将此格式化的字符串转换回UTC时间戳。这有效地将时间设置为同一天的 # 00:00:00UTC。time.strptime() # time.mktime() # 它返回表示一天开始的UTC时间戳。 # 总体而言,此函数可用于将任何UTC时间戳转换为UTC中相应日期的开始 def get_dh_height(self): try: with open('daily_height.csv', mode='r') as f: reader = csv.reader(f) daily_height = {rows[0]: rows[1] for rows in reader} return daily_height return None except: print("failed open daily_height.csv") return None # 该函数尝试从名为“daily_height.csv”的CSV # 文件中读取数据,并将其转换为字典,其中键是日期,值是相应的高度。get_dh_height # 其工作原理如下: # 它尝试在读取模式下打开文件“daily_height.csv”。 # 如果文件已成功打开,它将使用函数读取其内容,该函数将返回 # CSV文件各行的可迭代对象。csv.reader() # 然后,它将每行转换为键值对,其中第一列(索引0)表示日期,第二列(索引1)表示高度。这是使用字典理解来完成的。 # 返回生成的字典。daily_height # 如果在此过程中发生任何错误(例如无法打开文件),它会打印一条消息指示失败并返回. # None # 此函数可用于从CSV文件中检索每日身高数据,以便在程序中进行进一步处理或分析 def get_dh_hash(self): try: with open('daily_height.csv', mode='r') as f: reader = csv.reader(f) daily_hash = {rows[0]: rows[2] for rows in reader} return daily_hash return None except: print("failed open daily_height.csv") return None # 该函数类似于 ,但它不是检索高度值,而是从同一个CSV # 文件 “daily_height.csv” 中检索哈希值。get_dh_hashget_dh_height # 以下是其工作原理的细分: # 它尝试在读取模式下打开文件“daily_height.csv”。 # 如果文件已成功打开,它将使用函数读取其内容,该函数将返回 # CSV文件各行的可迭代对象。csv.reader() # 然后,它将每行转换为键值对,其中第一列(索引0)表示日期,第三列(索引2)表示哈希值。这是使用字典理解来完成的。 # 返回生成的字典。daily_hash # 如果在此过程中发生任何错误(例如无法打开文件),它会打印一条消息指示失败并返回. # None与 # 一样,此函数可用于从CSV文件中检索每日哈希数据,以便在程序中进一步处理或分析。get_dh_height def get_daily_height(self): height = 1 last_dayutc = None daily_height = self.get_dh_height() daily_hash = self.get_dh_hash() #print(daily_height) #print(daily_hash) if daily_height is None: daily_height = {} daily_hash = {} else: if len(daily_height) > 0: item = daily_height.popitem() #print(item, type(item[1])) height = int(item[1])+1 daily_height[item[0]] = int(item[1]) ip = "127.0.0.1" port = "8332" user = "user" password = "password" rpc = RPC(ip, port, user, password) while True: try: total_height = rpc.blockchain.get_block_count() break except Exception as e: print(f"❌ get_block_count 超时或异常:{e},重试中...") time.sleep(3) rpc = RPC(ip, port, user, password) # 重新建立连接 if height >= total_height: return prev_height = None prev_hash = None while True: blockh = None while True: try: blockh = rpc.blockchain.get_block_header(height, True) #print(blockh) break except: time.sleep(1) #print("reconnect") rpc = RPC(ip, port, user, password) if blockh is not None: block_time = blockh["time"] block_height = blockh["height"] block_hash = blockh["hash"] dayutc = self.get_day_utc(block_time) if last_dayutc is None: last_dayutc = dayutc print(dayutc, last_dayutc) if dayutc != last_dayutc: daily_height[str(last_dayutc)] = prev_height daily_hash[str(last_dayutc)] = prev_hash last_dayutc = dayutc #print(dayutc, daily_height[str(dayutc)], daily_hash[str(dayutc)]) prev_height = block_height prev_hash = block_hash while True: try: total_height = rpc.blockchain.get_block_count() if height == total_height: break else: height += 1 print("next height " + str(height)) break except: time.sleep(1) #print("reconnect") rpc = RPC(ip, port, user, password) if height == total_height: break with open('daily_height.csv', 'w') as f: for key in daily_height.keys(): #print(key) #print(daily_height[key]) #print(daily_hash[key]) f.write("%s, %s, %s\n" % (key, daily_height[key], daily_hash[key])) f.close() # 该函数负责从区块链中检索每日身高数据并将其存储在名为“daily_height.csv”的CSV文件中。其工作原理如下: # get_daily_height # 它使用默认值1初始化变量,并将其设置为 。heightlast_dayutcNone # 它调用 # 和 # 函数以从CSV文件中检索任何现有的每日高度和哈希数据。如果数据不可用,它将初始化空字典。get_dh_heightget_dh_hash # 如果存在现有的每日身高数据,它将检索最后一个条目,将身高递增1,并使用新的身高值更新字典。 # 它设置了与比特币区块链交互的RPC连接细节。 # 它使用RPC调用检索区块链的总高度。rpc.blockchain.get_block_count() # 它进入一个循环来获取每个区块高度的区块头,直到达到区块链的总高度。 # 在循环中,如果RPC调用期间出现异常,它会尝试重新连接,从而处理网络问题。 # 对于成功获取的每个区块标头,它都会提取区块时间、高度、哈希值和UTC日。 # 如果UTC日发生更改,它会使用当天以前的高度和哈希值更新每日高度和哈希字典。 # 它将每日高度和哈希数据写入“daily_height.csv”文件。 # 一旦它处理了所有区块高度,直到区块链的总高度,该函数就会结束。 # 该功能使用最新的区块链信息有效地更新CSV文件中的每日高度和哈希数据,方便历史数据分析或程序内的其他目的。 def get_height_timestamp(self): height = 0 height_time = self.get_ht() if height_time is None: height_time = {} else: height = len(height_time) print("exist height", height) ip = "127.0.0.1" port = "8332" user = "user" password = "password" rpc = RPC(ip, port, user, password) while True: try: total_height = rpc.blockchain.get_block_count() break except Exception as e: print(f"❌ get_block_count 超时或异常:{e},重试中...") time.sleep(10) rpc = RPC(ip, port, user, password) # 重新建立连接 if height >= total_height: return #total_height = rpc.blockchain.get_block_count() # print("last_height", total_height) while True: blockh = None while True: try: blockh = rpc.blockchain.get_block_header(height, True) #print(blockh) break except: time.sleep(1) print("reconnect") rpc = RPC(ip, port, user, password) if blockh is not None: block_time = blockh["time"] block_height = blockh["height"] height_time[str(block_height)] = block_time #print(str(block_height), height_time[str(block_height)]) while True: try: total_height = rpc.blockchain.get_block_count() if height == total_height: break else: height += 1 print("next height " + str(height)) break except: time.sleep(1) #print("reconnect") rpc = RPC(ip, port, user, password) if height == total_height: break with open('height_time.csv', 'w') as f: for key in height_time.keys(): f.write("%s, %s\n" % (key, height_time[key])) f.close() # 该函数从区块链中检索区块高度和相应的时间戳,并将它们存储在名为“height_time.csv”的CSV文件中。以下是其工作原理的细分: # get_height_timestamp # 它将变量初始化为0,并使用该函数检索现有的高度时间数据。如果不存在任何数据,则初始化一个空字典。heightget_ht # 它设置了与比特币区块链交互的RPC连接细节。 # 它使用RPC调用检索区块链的总高度。rpc.blockchain.get_block_count() # 如果存在现有的高度时间数据,则将变量设置为字典中的条目数。height # 它进入一个循环来获取每个区块高度的区块头,直到达到区块链的总高度。 # 在循环中,如果RPC调用期间出现异常,它会尝试重新连接,从而处理网络问题。 # 对于每个成功获取的块头,它都会提取块时间和高度,并更新高度时间字典。 # 它将高度时间数据写入“height_time.csv”文件。 # 循环一直持续到它处理完所有区块高度,直到区块链的总高度。 # 该功能有效地将CSV文件中的高度时间数据更新为最新的区块链信息,方便程序内的历史数据分析或其他目的。 def handle_utxos(self, prices, ht, check_dayutc): current_price = 0 #current_price = self.get_current_price() #if current_price == 0: # return connin = sqlite3.connect("utxos.sqlite") cin = connin.cursor() cursorin = cin.execute("SELECT * from utxos") #connout = sqlite3.connect("utxos.db") #cout = connout.cursor() #cout.execute('CREATE TABLE IF NOT EXISTS utxos(txid TEXT PRIMARY KEY NOT NULL, vout INT, value INT, coinbase INT, height INT, scriptpubkey TEXT, dt TIMESTAMP, price INT, dt2 TIMESTAMP)') #connout.commit() coin_idx = 0 rpc = None for row in cursorin: #print(row) txid = row[0] vout = row[1] value = row[2] coinbase = row[3] height = row[4] scriptpubkey = row[5] dt = ht[str(height)] #print(dt) dt2 = time.gmtime(int(dt)) daystr = time.strftime("%d %b %Y", dt2) dayutc = int(time.mktime(time.strptime(daystr, "%d %b %Y"))) price = 0 dayutc=dayutc-28800 current_price=current_price-28800 print("ceshi",str(dayutc)) print("ceshi2", str(current_price)) if str(dayutc) > str(check_dayutc): continue if str(dayutc) in prices: price = int(prices[str(dayutc)]) else: print("failed get tx price") return if str(check_dayutc) in prices: current_price = int(prices[str(check_dayutc)]) else: print("failed get check price") return value = value / 100000000 #print(self.current_dayutc, txid, value, coinbase, height, daystr, dayutc, price, current_price) self.summary_utxos(current_price, txid, vout, coinbase, value, height, scriptpubkey, dt, price, dayutc) self.summary_toplist(txid, vout, coinbase, value, height, scriptpubkey, dt, price, dayutc) #sql_insert = 'INSERT INTO utxos VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)' #insert_data = (txid, vout, value, coinbase, height, scriptpubkey, dt, price, dayutc) #cout.execute(sql_insert, insert_data) #if coin_idx % (1024 * 1024) == 0: #print(coin_idx) #connout.commit() coin_idx+=1 #connout.commit() if self.lth_rcap > 0: self.lth_mvrv = self.lth_mv / self.lth_rcap if self.sth_rcap > 0: self.sth_mvrv = self.sth_mv / self.sth_rcap self.save_db() cin.close() #cout.close() connin.close() #connout.close() # 该方法似乎用于处理来自名为“utxos.sqlite”的 # SQLite # 数据库的未使用的事务输出 (UTXO)。以下是其功能的细分:handle_utxos # 它需要三个参数:(包含历史价格的字典)、(包含区块高度和时间戳的字典)和(检查价格的特定日期)。priceshtcheck_dayutc # 它初始化为0和0。current_pricecoin_idx # 它建立与SQLite # 数据库的连接,并检索用于执行SQL查询的游标。 # 它循环访问从数据库提取的表中的每一行。utxos对于每一行,它提取各种属性,例如交易 # ID()、输出索引 ()、值、是否是coinbase交易 ()、区块高度 ()、scriptPubKey () 和时间戳 ()。txidvoutcoinbaseheightscriptpubkeydt # 它将时间戳转换为人类可读的日期字符串,然后转换为UTC时间戳。dtdaystrdayutc # 它从字典中获取与对应的历史价格,并将其分配给变量。dayutcpricesprice # 它检查字典中是否存在 ,并将其相应的价格分配给 。check_dayutcpricescurrent_price # 它将UTXO值除以1000000000以将其转换为比特币单位。 # 它调用和方法与相关参数来处理UTXO数据。summary_utxossummary_toplist # 如有必要,根据某些条件进行计算。lth_mvrvsth_mvrv # 最后,它调用该方法将处理后的数据保存到数据库中。save_db # 它关闭游标和数据库连接。 # 此方法有效地处理来自SQLite数据库的UTXO的处理,执行必要的计算和更新,并将处理后的数据保存回数据库 def utxos(self, check_dayutc): self.current_dt = check_dayutc self.current_dt2 = time.gmtime(int(self.current_dt)) self.current_daystr = time.strftime("%d %b %Y", self.current_dt2) self.current_dayutc = int(time.mktime(time.strptime(self.current_daystr, "%d %b %Y"))) topliststr = "toplist" + self.current_daystr print(topliststr) self.mdbc_toplist = self.mdb[topliststr] # prices_temp = self.get_history_price() # prices = self.get_history_price2(prices_temp) prices_temp=btc_historical_price.prices_temp prices=btc_historical_price.prices '''for key in prices.keys(): print(key, prices[key]) break''' print(prices) if len(prices) <= 0: print("failed get price") return ht = self.get_ht() if ht is None: print("failed get height and time") return self.handle_utxos(prices, ht, check_dayutc) # 该方法似乎负责处理 表示的特定日期的未使用事务输出 (UTXO)。以下是其功能的摘要:utxoscheck_dayutc # # 它根据提供的 .check_dayutc # 它使用 和 方法检索历史价格数据。get_history_priceget_history_price2 # 该方法从纳斯达克 API 检索历史比特币价格数据。get_history_price # 该方法从 Messari API 检索其他历史价格数据。get_history_price2 # 它使用该方法检索高度和时间戳数据。get_ht # 它调用该方法以使用检索到的价格和高度数据处理 UTXO。handle_utxos # 该方法进一步与 SQLite 数据库交互以处理 UTXO 并执行必要的计算。handle_utxos # 总体而言,该方法协调了获取历史价格和高度数据并使用它来处理特定日期的 UTXO 的过程。如果成功,它将使用处理后的 UTXO 数据更新相关数据库。utxos if __name__ == '__main__': if len(sys.argv) > 1: check_dt = sys.argv[1] stats = UtxosIf() setup('mainnet') stats.get_height_timestamp() stats.get_daily_height() daily_hash = stats.get_dh_hash() #print(daily_hash) if daily_hash is None: print("failed get daily height") else: os.system("if [ -e utxos.dat ]; then rm utxos.dat; fi") os.system("if [ -e utxos.sqlite ]; then rm utxos.sqlite; fi") check_dayutc = int(time.mktime(time.strptime(check_dt, "%Y-%m-%d"))) cmd = "~/bitcoin-29.0/bin/bitcoin-cli -rpcuser=user -rpcpassword=password invalidateblock " + daily_hash[str(check_dayutc)] os.system(cmd) print("select ok") time.sleep(60); os.system("~/bitcoin-29.0/bin/bitcoin-cli -rpcuser=user -rpcpassword=password dumptxoutset ~/utxos.dat") print("dumptxoutset ok") time.sleep(60); # os.system("./utxo_to_sqlite ./utxos.dat ./utxos.sqlite") os.system("python3 ~/utxo_to_sqlite.py ~/utxos.dat ~/utxos.sqlite") print("utxo_to_sqlite ok") time.sleep(60); stats.utxos(check_dayutc) print("utxos stat ok") cmd = "~/bitcoin-29.0/bin/bitcoin-cli -rpcuser=user -rpcpassword=password reconsiderblock " + daily_hash[str(check_dayutc)] os.system(cmd) print("reconsiderblock ok") # 此脚本似乎基于命令行参数执行与比特币 UTXO(未花费的交易输出)相关的几个任务。以下是它的作用的细分: # 1. 它检查脚本是否直接运行 ('__name__ == '__main__'')。 # 2. 它检查是否提供了命令行参数 ('len(sys.argv) > 1')。 # 3. 它初始化“UtxosIf”类的实例,可能包含与处理 UTXO 相关的方法。 # 4. 它使用“设置”功能设置比特币网络(假设为“主网”)。 # 5. 它分别使用“get_height_timestamp”和“get_daily_height”方法检索高度和时间戳数据。 # 6. 它使用“get_dh_hash”方法获取每日哈希数据。 # 7. 如果每日哈希数据可用,则继续进行进一步操作: # - 如果存在现有的“utxos.dat”和“utxos.sqlite”文件,它会删除它们。 # - 它使用比特币 CLI 命令“invalidateblock”使区块失效。 # - 它会等待一段时间(大概是网络处理区块失效)。 # - 它使用比特币CLI命令'dumptxoutset'转储UTXO集。 # - 它再次等待。 # - 它使用“utxo_to_sqlite”脚本将转储的 UTXO 数据转换为 SQLite 数据库。 # - 它使用“utxos”方法处理指定日期的 UTXO。 # - 它使用比特币 CLI 命令“reconsiderblock”重新考虑之前失效的区块。 # 8. 每个步骤都附有指示脚本进度的打印语句。 # 这个脚本似乎是一个更大的系统的一部分,该系统与比特币网络交互并执行与UTXO相关的任务。它严重依赖比特币核心软件和命令行界面(“bitcoin-cli”)