diff --git a/coinbus/btc_utxos_lyq2.py b/coinbus/btc_utxos_lyq2.py new file mode 100644 index 0000000..aff4f61 --- /dev/null +++ b/coinbus/btc_utxos_lyq2.py @@ -0,0 +1,1730 @@ +# 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”) \ No newline at end of file