coinbus-data/coinbus/btc_stats_qt.py

1220 lines
71 KiB
Python
Raw Normal View History

2025-07-25 09:29:12 +00:00
# coding=utf-8
import sys
import time
from easybitcoinrpc import RPC
from bitcoinutils.setup import setup
from bitcoinutils.script import Script
from bitcoinutils.keys import P2wpkhAddress, P2wshAddress, P2shAddress, PrivateKey, PublicKey, SegwitAddress, \
P2pkhAddress
import requests
import ujson
from requests import Session
from requests.exceptions import ConnectionError, Timeout, TooManyRedirects
import db_if_qt
import redis_if_qt
import pymysql
DEF_CONFIG_RULES = "rules"
# 此脚本似乎是一个包含导入和一些默认配置的 Python 模块。以下是其内容的细分:
#
# 1. **导入语句**
# - 'sys':提供对 Python 解释器使用或维护的某些变量以及与解释器交互的函数的访问。
# - 'time':提供各种与时间相关的功能。
# - 'easybitcoinrpc':它似乎是一个自定义模块或库,用于通过 RPC远程过程调用与 Bitcoin Core 进行交互。
# - 'bitcoinutils':另一个用于与比特币交互的库,提供用于处理密钥、地址、脚本等的实用程序。
# - 'requests':一个流行的 HTTP 库,用于发出请求。
# - 'ujson'一个快速的JSON编码器和解码器兼容Python的内置'json'模块。
# - 'Session' 'ConnectionError' 'Timeout' 'TooManyRedirects'这些是“requests”库中用于处理 HTTP 请求和错误的特定组件。
#
# 2. **默认配置**
# - “DEF_CONFIG_RULES”默认配置规则设置为“规则”。这可能表示与规则相关的内容的默认值或配置。
class StatIf:
def __init__(self, ip="127.0.0.1", port="8332", user="user", password="password"):
self.host = ip
self.port = port
self.user = user
self.pwd = password
self.rpc = None
self.height = 0
self.pricedict = {}
setup('mainnet')
# 该类似乎是用于处理与比特币节点或区块链相关的统计数据的更大系统的一部分。以下是其属性和构造函数的细分StatIf
# 特性:
# host表示Bitcoin
# RPC服务器IP地址的字符串。默认值为 。"127.0.0.1"
# port表示Bitcoin
# RPC服务器端口号的字符串。默认值为 。"8332"
# user一个字符串表示用于向Bitcoin
# RPC服务器进行身份验证的用户名。默认值为 。"user"
# pwd一个字符串表示用于使用BitcoinRPC服务器进行身份验证的密码。默认值为 。"password"
# rpc初始化为 连接后将保存RPC客户端的实例。None
# height初始化为 它将存储区块链的当前高度。0
# 构造函数:
# 构造函数使用默认值或作为参数传递的值初始化属性。
# 它还初始化属性该属性似乎用于存储与价格相关的数据。pricedict
# 最后,它调用 这可能会设置与比特币主网络交互的环境。setup('mainnet')
# 此类作为处理统计信息和通过RPC与比特币节点交互的基础。通常会添加其他方法和功能来执行特定任务例如检索区块信息、查询交易数据等。
def get_vin_address(self, prev_scriptpubkey, prev_height, txid):
prev_type = prev_scriptpubkey["type"]
prev_address = None
if prev_type != "nulldata":
if isinstance(prev_scriptpubkey, dict):
if "address" in prev_scriptpubkey:
prev_address = prev_scriptpubkey["address"]
else:
if prev_scriptpubkey.is_address():
prev_address = prev_scriptpubkey["address"]
if prev_address is None:
if prev_type == "pubkey":
temphex = prev_scriptpubkey["hex"]
try:
if temphex[2:4] == "04":
prev_address = PublicKey(temphex[2:132]).get_address(False).to_string()
elif temphex[2:4] == "02" or temphex[2:4] == "03":
prev_address = PublicKey(temphex[2:68]).get_address().to_string()
except:
print("decode address failed", str(prev_height), "txid", txid, "hex", temphex)
if prev_address is None:
prev_address = prev_scriptpubkey["hex"]
return prev_address
# “StatIf”类中的“get_vin_address”方法似乎旨在检索与事务输入 vin 关联的地址。以下是其工作原理的细分:
# - ** 参数 **
# - 'prev_scriptpubkey'输入正在花费的上一个事务输出prevout的scriptPubKey。
# - 'prev_height':包含前一笔交易输出的区块的高度。
# - 'txid'包含正在分析的输入的交易的交易ID。
# - ** 功能性 **
# - 它首先确定scriptPubKey 'prev_type' 的类型。
# - 如果scriptPubKey类型不是“nulldata”表示它不是非标准或不可花费的输出
# - 它检查“prev_scriptpubkey”是否是字典以及它是否包含“address”键。如果是这样它会从那里检索地址。
# - 如果“prev_scriptpubkey”不是字典或不包含“address”键则会直接检查它是否为有效地址。
# - 如果地址仍为“None”则会将scriptPubKey类型视为“pubkey”并尝试从公钥的十六进制表示形式派生地址。
# - 如果所有检索地址的尝试都失败则默认返回scriptPubKey的十六进制表示形式。
# 此方法旨在用于识别交易记录输入的支出地址。它处理不同类型的
# scriptPubKey包括标准地址和公钥。如果遇到任何解码错误它会打印一条指示失败的消息以及相关信息例如区块高度和事务ID。
def get_vout_address(self, scriptpubkey, height, txid):
return self.get_vin_address(scriptpubkey, height, txid)
# “StatIf”类中的“get_vout_address”方法似乎是“get_vin_address”方法的简单包装器。它实质上将检索与事务输出 vout 关联的地址的任务委托给“get_vin_address”方法。
# 以下是它的作用:
# - ** 参数 **
# - 'scriptpubkey'此参数表示当前正在使用的输出的scriptPubKey。
# - 'height':包含输出的块的高度。
# - 'txid'当前输出的事务ID。
# - ** 功能性 **
# - 它只是使用相同的参数('scriptpubkey' 'height' 'txid')调用'get_vin_address'方法。
# - ** 返回 **
# - 它返回“get_vin_address”方法返回的任何内容。
# 此方法在检索事务输入和输出关联的添加时提供处理事务输入和输出的一致性
def get_history_price(self, batch_size=5000):
#pricedict = {}
# response_price = requests.get(
# 'https://data.nasdaq.com/api/v3/datasets/BCHAIN/MKPRU.json?api_key=FZqXog4sR-b7cYnXcRVV')
# if response_price.status_code == 200:
# priceweb = ujson.loads(response_price.content)
# if "dataset" in priceweb:
# priceset = priceweb["dataset"]
# if "data" in priceset:
# pricedata = priceset["data"]
# for price in pricedata:
# daystr = price[0]
# p = price[1]
# dayutc = time.mktime(time.strptime(daystr, "%Y-%m-%d"))
# self.pricedict[str(int(dayutc))] = float(p)
# if len(self.pricedict) > 0:
# return self.pricedict
#
# response_price.close()
#return self.pricedict
"""获取数据库中的 Nasdaq 数据,存入字典"""
db_config = {
"host": "192.168.194.216",
"user": "root",
"password": "2GS@bPYcgiMyL14A",
"database": "btcdb",
"port": 4423,
"connect_timeout": 60,
"read_timeout": 60,
"write_timeout": 60,
"charset": "utf8mb4"
}
offset = 0
self.pricedict = {}
while True:
connection = pymysql.connect(**db_config)
try:
with connection.cursor() as cursor:
sql = "SELECT timestamp, price FROM btc_prices WHERE source = 'Nasdaq' ORDER BY timestamp LIMIT %s OFFSET %s"
cursor.execute(sql, (batch_size, offset))
rows = cursor.fetchall()
if not rows:
break
for timestamp, price in rows:
self.pricedict[str(int(timestamp))] = float(price)
finally:
connection.close()
offset += batch_size
if len(rows) < batch_size:
break # 最后一页读取完成
return self.pricedict
# “get_history_price”方法似乎从特定数据源中检索历史比特币价格并将其存储在类实例 “self.pricedict” 的“pricedict”属性中。以下是该方法的细分
# - ** 功能性 **
# - 它向特定URL ''https: // data.nasdaq.com / api / v3 / datasets / BCHAIN / MKPRU.json?api_key = FZqXog4sR - b7cYnXcRVV'' 发送GET请求以获取历史比特币价格数据。
# - 如果响应状态代码为“200”表示成功
# - 它使用“ujson.loads”方法解析JSON响应。
# - 它从解析的JSON响应中检索相关数据并对其进行迭代。
# - 对于数据中的每个条目它提取日期和价格将日期转换为Unix时间戳并将价格存储在'pricedict'字典属性中以Unix时间戳为键。
# - 最后,如果'pricedict'在处理数据后不为空,则返回字典。
# - ** 返回 **
# - 如果成功它将返回“pricedict”字典其中包含由Unix时间戳索引的历史比特币价格。
# - ** 注意 **
# - 方法末尾注释掉的代码 '#return self.pricedict' 似乎是返回'pricedict'的替代方法,但目前已被禁用。您可以选择取消注释并使用它,而不是显式返回。
def get_history_price2(self, batch_size=5000):
# #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"
#
# if end_year != year:
# url = url + str(start_year) + "-01-01&end=" + str(end_year) + "-12-31&order=descending"
# 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")))
# self.pricedict[str(ret_dayutc)] = float(s)
# # print(s, dayutc, pricedict[str(dayutc)])
# # break
# else:
# break
# else:
# break
# end_year -= 1
# time.sleep(2)
# self.pricedict[str(1308528000)]=float(15.5)
# self.pricedict[str(1308614400)] = float(15.05)
# self.pricedict[str(1308700800)] = float(15.39)
# self.pricedict[str(1308787200)] = float(16.7501)
# self.pricedict[str(1308873600)] = float(17.6)
# self.pricedict[str(1308960000)] = float(16.95)
# print(self.pricedict)
# return self.pricedict
"""获取数据库中的 Messari 数据,存入字典"""
db_config = {
"host": "192.168.194.216",
"user": "root",
"password": "2GS@bPYcgiMyL14A",
"database": "btcdb",
"port": 4423,
"connect_timeout": 60,
"read_timeout": 60,
"write_timeout": 60,
"charset": "utf8mb4"
}
offset = 0
self.pricedict = {}
while True:
connection = pymysql.connect(**db_config)
try:
with connection.cursor() as cursor:
sql = """
SELECT timestamp, price
FROM btc_prices
WHERE source = 'CryptoCompare'
ORDER BY timestamp
LIMIT %s OFFSET %s
"""
cursor.execute(sql, (batch_size, offset))
rows = cursor.fetchall()
if not rows:
break
for timestamp, price in rows:
self.pricedict[str(int(timestamp))] = float(price)
finally:
connection.close()
offset += batch_size
if len(rows) < batch_size:
break # 数据已全部读取
return self.pricedict
# “get_history_price2”方法似乎从另一个数据源检索历史比特币价格并将它们存储在类实例 'self.pricedict' 的“pricedict”属性中。以下是该方法的说明
# - ** 功能性 **
# - 它初始化与当前日期相关的变量('dayt'、'daystr'、'year'、'end_year')。
# - 它进入一个循环,从当前年份开始,回到过去,在几年内迭代。
# - 它根据当前年份构建一个URL并发送一个GET请求以从MessariAPI检索历史比特币价格数据。
# - 如果响应状态码为“200”表示成功则解析JSON响应。
# - 它从JSON响应中提取相关数据时间戳和价格并将时间戳转换为Unix时间戳。
# - 它将价格存储在“pricedict”字典属性中并以Unix时间戳为键。
# - 循环继续,直到检索到所有所需年份的数据或遇到错误。
# - 它打印“pricedict”用于调试目的并返回它。
# - ** 返回 **
# - 该方法返回包含由Unix时间戳索引的历史比特币价格的“pricedict”字典。
# - ** 注意 **
# - 此方法从MessariAPI获取数据其结构类似于从Nasdaq API获取数据的“get_history_price”。这两种方法都用于检索历史比特币价格但来源不同。
def get_current_utc(self):
curtime = time.gmtime(time.time())
daystr = time.strftime("%d %b %Y", curtime)
dayutc = int(time.mktime(time.strptime(daystr, "%d %b %Y")))
return dayutc
# “get_current_utc”方法检索当前UTC时间戳。以下是其功能的细分
# - ** 功能性 **
# - 它使用'time'模块使用'time.gmtimetime.time'获取UTC中的当前时间。
# - 它将当前时间格式化为一个字符串以“DDMonYYYY”的格式表示日期例如“2024年3月15日”
# - 它使用“time.mktime”将格式化的字符串转换回Unix时间戳。
# - 最后它返回以UTC表示当前日期的Unix时间戳。
# - ** 返回 **
# - 该方法返回一个整数以UTC表示当天的时间戳。
# 此方法可用于获取UTC中的当前时间戳以用于各种目的例如日志记录、时间戳事件或跨不同系统同步操作。
def get_current_price(self):
price = 0
DB_CONFIG = {
"host": "192.168.194.216",
"user": "root",
"password": "2GS@bPYcgiMyL14A",
"database": "btcdb",
"port": 4423
}
connection = pymysql.connect(**DB_CONFIG)
try:
with connection.cursor() as cursor:
for source in ("binance", "coinbase"):
cursor.execute("""
SELECT price FROM btc_realtime_prices
WHERE source=%s
ORDER BY timestamp DESC
LIMIT 1
""", (source,))
row = cursor.fetchone()
if row:
price = float(row[0])
break
finally:
connection.close()
return price
# 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
# “get_current_price”方法旨在从特定的API端点获取以美元为单位的比特币的当前价格。以下是其功能的说明
# - ** 功能性 **
# - 它向指定的URL发送HTTPGET请求“https: // bitcoinexplorer.org / api / price”。
# - 如果响应状态代码为200表示成功则使用'ujson.loadsresponse_price.text'从响应中提取JSON数据。
# - 然后它从JSON数据中检索以美元为单位的比特币价格并将其转换为浮点数。
# - 最后,它返回提取的价格。
# - ** 返回 **
# - 该方法返回一个浮点数,表示比特币的当前美元价格。
# 此方法可用于获取各种应用程序的实时比特币价格数据,例如财务分析、加密货币交易或向用户显示价格信息。
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
# “get_day_utc”函数在000000时将给定的UTC时间戳转换为相应的UTC日期自Unix纪元以来的秒数。以下是其功能的细分
# - ** 参数 **
# - 'utc_time'表示要转换的UTC时间戳的整数。
# - ** 功能性 **
# - 它使用“time.gmtimeutc_time”将提供的UTC时间戳转换为UTC时区的时间元组。
# - 然后,它使用'time.strftime“%d %b %Y” t'将UTC时间元组格式化为表示日期的字符串格式为“DDMonYYYY”例如“01Jan2024”
# - 最后,它使用'time.mktimetime.strptimedaystr “%d %b %Y”'将格式化的日期字符串转换回UTC时间戳。
# - ** 返回 **
# - 该函数返回一个整数表示所提供日期在000000小时的UTC时间戳。
# 此函数可用于将时间戳转换为相应的UTC日期这对于各种应用程序例如数据分析、时间序列操作或基于UTC时间戳生成每日报告非常有用。
def rpc_cmd(self, cmd):
if self.rpc is None:
self.rpc = RPC(self.host, self.port, self.user, self.pwd)
while True:
try:
if cmd == "getblockstats":
getblockstats = self.rpc.blockchain.get_block_stats(self.height)
return getblockstats
elif cmd == "getblock":
getblock = self.rpc.blockchain.get_block(self.height, 3)
return getblock
elif cmd == "getblockcount":
getblockcount = self.rpc.blockchain.get_block_count()
if self.height == getblockcount:
time.sleep(30)
else:
self.height += 1
print("next height", self.height)
return None
elif cmd == "getmempoolinfo":
getmempoolinfo = self.rpc.blockchain.get_mempool_info()
return getmempoolinfo
else:
return None
except:
time.sleep(1)
self.rpc = RPC(self.host, self.port, self.user, self.pwd)
# “rpc_cmd”方法旨在对比特币核心节点执行各种RPC命令。以下是其功能的细分
# - ** 参数 **
# - 'cmd'表示要执行的RPC命令的字符串。
# - ** 功能性 **
# - 它首先检查RPC连接是否已建立 'self.rpc'。如果没有它将使用提供的主机、端口、用户名和密码初始化RPC连接。
# - 然后它进入一个循环以处理潜在的连接错误并重试执行RPC命令。
# - 根据'cmd'的值它执行不同的RPC命令
# - 如果'cmd'是 “getblockstats”它会使用'rpc.blockchain.get_block_statsself.height'检索当前块高度 'self.height' 的块统计信息。
# - 如果'cmd'为 “getblock”则使用'rpc.blockchain.get_blockself.height 3'以详细格式 '3' 检索当前块高度的块信息。
# - 如果'cmd'是 “getblockcount”它会使用'rpc.blockchain.get_block_count'检索当前块计数。如果当前高度与块计数匹配则等待30秒然后再次检查;否则它会递增高度并返回“None”。
# - 如果'cmd'是 “getmempoolinfo”它会使用'rpc.blockchain.get_mempool_info'检索有关内存池的信息。
# - 如果无法识别“cmd”则返回“None”。
# - ** 返回 **
# - 该方法返回RPC命令执行的结果如果发生错误或无法识别命令则返回“None”。
# 这种方法提供了一种通过RPC命令与比特币核心节点进行交互的灵活方式并通过在短时间延迟后重试命令来优雅地处理潜在的连接问题。
def stat_load(self, redisif, config):
self.rules = config[DEF_CONFIG_RULES]
self.get_history_price()
self.history_prices = self.get_history_price2()
self.current_price = self.get_current_price()
self.current_utc = self.get_current_utc()
self.history_prices[str(self.current_utc)] = self.current_price
self.daily_date = redisif.get_btc_data("daily_date")
if self.daily_date is None:
self.stat_reset()
return
self.daily_date = int(redisif.get_btc_data("daily_date"))
self.daily_height = int(redisif.get_btc_data("daily_height").decode("utf-8"))
self.height = self.daily_height
self.daily_height_begin = int(redisif.get_btc_data("daily_height_begin").decode("utf-8"))
self.daily_height_end = int(redisif.get_btc_data("daily_height_end").decode("utf-8"))
self.daily_date_string = redisif.get_btc_data("daily_date_string").decode("utf-8")
self.daily_profit = float(redisif.get_btc_data("daily_profit").decode("utf-8"))
self.daily_fees = float(redisif.get_btc_data("daily_fees").decode("utf-8"))
self.daily_txs = int(redisif.get_btc_data("daily_txs").decode("utf-8"))
self.daily_new_address = int(redisif.get_btc_data("daily_new_address").decode("utf-8"))
self.daily_total_address = int(redisif.get_btc_data("daily_total_address").decode("utf-8"))
self.daily_new_address_volume = float(redisif.get_btc_data("daily_new_address_volume").decode("utf-8"))
self.daily_active_address = int(redisif.get_btc_data("daily_active_address").decode("utf-8"))
self.daily_send_address = int(redisif.get_btc_data("daily_send_address").decode("utf-8"))
self.daily_receive_address = int(redisif.get_btc_data("daily_receive_address").decode("utf-8"))
self.daily_volume = float(redisif.get_btc_data("daily_volume").decode("utf-8"))
self.daily_eavolume = float(redisif.get_btc_data("daily_eavolume").decode("utf-8"))
self.daily_asol = float(redisif.get_btc_data("daily_asol").decode("utf-8"))
self.daily_eaasol = float(redisif.get_btc_data("daily_eaasol").decode("utf-8"))
self.daily_atxs = float(redisif.get_btc_data("daily_atxs").decode("utf-8"))
self.daily_sopr_buy = float(redisif.get_btc_data("daily_sopr_buy").decode("utf-8"))
self.daily_asopr_buy = float(redisif.get_btc_data("daily_asopr_buy").decode("utf-8"))
self.daily_easopr_buy = float(redisif.get_btc_data("daily_easopr_buy").decode("utf-8"))
self.daily_lthsopr_buy = float(redisif.get_btc_data("daily_lthsopr_buy").decode("utf-8"))
self.daily_sthsopr_buy = float(redisif.get_btc_data("daily_sthsopr_buy").decode("utf-8"))
self.daily_sopr_sell = float(redisif.get_btc_data("daily_sopr_sell").decode("utf-8"))
self.daily_asopr_sell = float(redisif.get_btc_data("daily_asopr_sell").decode("utf-8"))
self.daily_easopr_sell = float(redisif.get_btc_data("daily_easopr_sell").decode("utf-8"))
self.daily_lthsopr_sell = float(redisif.get_btc_data("daily_lthsopr_sell").decode("utf-8"))
self.daily_sthsopr_sell = float(redisif.get_btc_data("daily_sthsopr_sell").decode("utf-8"))
self.daily_cdd = float(redisif.get_btc_data("daily_cdd").decode("utf-8"))
self.daily_sacdd = float(redisif.get_btc_data("daily_sacdd").decode("utf-8"))
self.daily_eacdd = float(redisif.get_btc_data("daily_eacdd").decode("utf-8"))
self.daily_cdd_days1 = float(redisif.get_btc_data("daily_cdd_days1").decode("utf-8"))
self.daily_cdd_days7 = float(redisif.get_btc_data("daily_cdd_days7").decode("utf-8"))
self.daily_cdd_days30 = float(redisif.get_btc_data("daily_cdd_days30").decode("utf-8"))
self.daily_cdd_days60 = float(redisif.get_btc_data("daily_cdd_days60").decode("utf-8"))
self.daily_cdd_days90 = float(redisif.get_btc_data("daily_cdd_days90").decode("utf-8"))
self.daily_cdd_days180 = float(redisif.get_btc_data("daily_cdd_days180").decode("utf-8"))
self.daily_cdd_days365 = float(redisif.get_btc_data("daily_cdd_days365").decode("utf-8"))
self.daily_cdd_days730 = float(redisif.get_btc_data("daily_cdd_days730").decode("utf-8"))
self.daily_csupply = float(redisif.get_btc_data("daily_csupply").decode("utf-8"))
self.daily_mintusd = float(redisif.get_btc_data("daily_mintusd").decode("utf-8"))
self.daily_sumcsupply = float(redisif.get_btc_data("daily_sumcsupply").decode("utf-8"))
self.daily_sumcdd = float(redisif.get_btc_data("daily_sumcdd").decode("utf-8"))
self.daily_sumeacdd = float(redisif.get_btc_data("daily_sumeacdd").decode("utf-8"))
self.daily_rprofit = float(redisif.get_btc_data("daily_rprofit").decode("utf-8"))
self.daily_rloss = float(redisif.get_btc_data("daily_rloss").decode("utf-8"))
self.daily_marketcap = float(redisif.get_btc_data("daily_marketcap").decode("utf-8"))
self.daily_rcap = float(redisif.get_btc_data("daily_rcap").decode("utf-8"))
self.daily_earcap = float(redisif.get_btc_data("daily_earcap").decode("utf-8"))
self.daily_mvrv = float(redisif.get_btc_data("daily_mvrv").decode("utf-8"))
'''self.daily_lth_marketcap = float(redisif.get_btc_data("daily_lth_marketcap").decode("utf-8"))
self.daily_lth_rcap = float(redisif.get_btc_data("daily_lth_rcap").decode("utf-8"))
self.daily_lth_mvrv = float(redisif.get_btc_data("daily_lth_mvrv").decode("utf-8"))
self.daily_sth_marketcap = float(redisif.get_btc_data("daily_sth_marketcap").decode("utf-8"))
self.daily_sth_rcap = float(redisif.get_btc_data("daily_sth_rcap").decode("utf-8"))
self.daily_sth_mvrv = float(redisif.get_btc_data("daily_sth_mvrv").decode("utf-8"))'''
self.daily_nupl = float(redisif.get_btc_data("daily_nupl").decode("utf-8"))
#self.daily_uprofit = float(redisif.get_btc_data("daily_uprofit").decode("utf-8"))
#self.daily_uloss = float(redisif.get_btc_data("daily_uloss").decode("utf-8"))
#self.daily_lthnupl = float(redisif.get_btc_data("daily_lthnupl").decode("utf-8"))
#self.daily_sthnupl = float(redisif.get_btc_data("daily_sthnupl").decode("utf-8"))
self.daily_price = self.get_price(self.height, self.daily_date)
#v2
self.daily_mint = float(redisif.get_btc_data("daily_mint").decode("utf-8"))
self.daily_lth_volume = float(redisif.get_btc_data("daily_lth_volume").decode("utf-8"))
self.daily_frm = float(redisif.get_btc_data("daily_frm").decode("utf-8"))
self.daily_cvdd = float(redisif.get_btc_data("daily_cvdd").decode("utf-8"))
self.daily_nvt_ratio = float(redisif.get_btc_data("daily_nvt_ratio").decode("utf-8"))
self.daily_balanced_price = float(redisif.get_btc_data("daily_balanced_price").decode("utf-8"))
self.daily_velocity = float(redisif.get_btc_data("daily_velocity").decode("utf-8"))
self.daily_mempool_volume = float(redisif.get_btc_data("daily_mempool_volume").decode("utf-8"))
self.daily_realized_price = float(redisif.get_btc_data("daily_realized_price").decode("utf-8"))
self.daily_transferred_price = float(redisif.get_btc_data("daily_transferred_price").decode("utf-8"))
#v2
self.daily_sumvdd = float(redisif.get_btc_data("daily_sumvdd").decode("utf-8"))
self.daily_sumdays = float(redisif.get_btc_data("daily_sumdays").decode("utf-8"))
# “stat_load”方法似乎负责从Redis数据库加载各种统计信息和历史数据。以下是它的作用的细分
# - ** 参数 **
# - 'redisif'提供Redis数据库接口功能的类的实例。
# - 'config':包含配置参数(包括规则)的字典。
# - ** 功能性 **
# - 从提供的“config”字典中加载配置规则。
# - 使用“get_history_price”和“get_history_price2”方法从外部来源检索历史价格数据。
# - 分别使用“get_current_price”和“get_current_utc”方法检索当前价格和当前UTC时间。
# - 使用当前价格和UTC时间更新历史价格数据。
# - 使用特定键从Redis数据库中检索各种每日统计数据和值并将它们存储在相应的实例属性中。
# - 根据需要对检索到的数据执行类型转换。
# - 此方法似乎可以同时处理第1版和第2版的统计数据如注释掉的部分所示。
# - ** 返回 **
# - 此方法没有显式返回值。
# 总体而言“stat_load”是一种关键的初始化方法用于加载在应用程序上下文中进一步分析或处理所需的相关数据和统计数据
def stat_save(self, redisif):
redisif.set_btc_data("daily_date", self.daily_date)
self.daily_height = self.height
redisif.set_btc_data("daily_height", self.daily_height)
redisif.set_btc_data("daily_height_begin", self.daily_height_begin)
redisif.set_btc_data("daily_height_end", self.daily_height_end)
redisif.set_btc_data("daily_date_string", self.daily_date_string)
redisif.set_btc_data("daily_profit", self.daily_profit)
redisif.set_btc_data("daily_fees", self.daily_fees)
redisif.set_btc_data("daily_txs", self.daily_txs)
redisif.set_btc_data("daily_new_address", self.daily_new_address)
redisif.set_btc_data("daily_total_address", self.daily_total_address)
redisif.set_btc_data("daily_new_address_volume", self.daily_new_address_volume)
redisif.set_btc_data("daily_active_address", self.daily_active_address)
redisif.set_btc_data("daily_send_address", self.daily_send_address)
redisif.set_btc_data("daily_receive_address", self.daily_receive_address)
redisif.set_btc_data("daily_volume", self.daily_volume)
redisif.set_btc_data("daily_eavolume", self.daily_eavolume)
redisif.set_btc_data("daily_asol", self.daily_asol)
redisif.set_btc_data("daily_eaasol", self.daily_eaasol)
redisif.set_btc_data("daily_atxs", self.daily_atxs)
redisif.set_btc_data("daily_sopr_buy", self.daily_sopr_buy)
redisif.set_btc_data("daily_asopr_buy", self.daily_asopr_buy)
redisif.set_btc_data("daily_easopr_buy", self.daily_easopr_buy)
redisif.set_btc_data("daily_lthsopr_buy", self.daily_lthsopr_buy)
redisif.set_btc_data("daily_sthsopr_buy", self.daily_sthsopr_buy)
redisif.set_btc_data("daily_sopr_sell", self.daily_sopr_sell)
redisif.set_btc_data("daily_asopr_sell", self.daily_asopr_sell)
redisif.set_btc_data("daily_easopr_sell", self.daily_easopr_sell)
redisif.set_btc_data("daily_lthsopr_sell", self.daily_lthsopr_sell)
redisif.set_btc_data("daily_sthsopr_sell", self.daily_sthsopr_sell)
redisif.set_btc_data("daily_cdd", self.daily_cdd)
redisif.set_btc_data("daily_sacdd", self.daily_sacdd)
redisif.set_btc_data("daily_eacdd", self.daily_eacdd)
redisif.set_btc_data("daily_cdd_days1", self.daily_cdd_days1)
redisif.set_btc_data("daily_cdd_days7", self.daily_cdd_days7)
redisif.set_btc_data("daily_cdd_days30", self.daily_cdd_days30)
redisif.set_btc_data("daily_cdd_days60", self.daily_cdd_days60)
redisif.set_btc_data("daily_cdd_days90", self.daily_cdd_days90)
redisif.set_btc_data("daily_cdd_days180", self.daily_cdd_days180)
redisif.set_btc_data("daily_cdd_days365", self.daily_cdd_days365)
redisif.set_btc_data("daily_cdd_days730", self.daily_cdd_days730)
redisif.set_btc_data("daily_csupply", self.daily_csupply)
redisif.set_btc_data("daily_mintusd", self.daily_mintusd)
redisif.set_btc_data("daily_sumcsupply", self.daily_sumcsupply)
redisif.set_btc_data("daily_sumcdd", self.daily_sumcdd)
redisif.set_btc_data("daily_sumeacdd", self.daily_sumeacdd)
redisif.set_btc_data("daily_rprofit", self.daily_rprofit)
redisif.set_btc_data("daily_rloss", self.daily_rloss)
redisif.set_btc_data("daily_marketcap", self.daily_marketcap)
redisif.set_btc_data("daily_rcap", self.daily_rcap)
redisif.set_btc_data("daily_earcap", self.daily_earcap)
redisif.set_btc_data("daily_mvrv", self.daily_mvrv)
'''redisif.set_btc_data("daily_lth_marketcap", self.daily_lth_marketcap)
redisif.set_btc_data("daily_lth_rcap", self.daily_lth_rcap)
redisif.set_btc_data("daily_lth_mvrv", self.daily_lth_mvrv)
redisif.set_btc_data("daily_sth_marketcap", self.daily_sth_marketcap)
redisif.set_btc_data("daily_sth_rcap", self.daily_sth_rcap)
redisif.set_btc_data("daily_sth_mvrv", self.daily_sth_mvrv)'''
redisif.set_btc_data("daily_nupl", self.daily_nupl)
#redisif.set_btc_data("daily_uprofit", self.daily_uprofit)
#redisif.set_btc_data("daily_uloss", self.daily_uloss)
#redisif.set_btc_data("daily_lthnupl", self.daily_lthnupl)
#redisif.set_btc_data("daily_sthnupl", self.daily_sthnupl)
#v2
redisif.set_btc_data("daily_mint", self.daily_mint)
redisif.set_btc_data("daily_lth_volume", self.daily_lth_volume)
redisif.set_btc_data("daily_frm", self.daily_frm)
redisif.set_btc_data("daily_cvdd", self.daily_cvdd)
redisif.set_btc_data("daily_nvt_ratio", self.daily_nvt_ratio)
redisif.set_btc_data("daily_balanced_price", self.daily_balanced_price)
redisif.set_btc_data("daily_velocity", self.daily_velocity)
redisif.set_btc_data("daily_mempool_volume", self.daily_mempool_volume)
redisif.set_btc_data("daily_realized_price", self.daily_realized_price)
redisif.set_btc_data("daily_transferred_price", self.daily_transferred_price)
redisif.set_btc_data("daily_sumvdd", self.daily_sumvdd)
redisif.set_btc_data("daily_sumdays", self.daily_sumdays)
# stat_save”方法负责将各种统计信息和值保存到Redis数据库。下面是该方法的每个部分的作用
# - ** 参数 **
# - 'redisif'提供Redis数据库接口功能的类的实例。
# - ** 功能性 **
# - 在Redis数据库中设置各种键值对对应不同的每日统计数据和值。
# - 该方法遍历每个统计量,并调用'redisif'对象的'set_btc_data'方法,将值保存在相应的键下。
# - 注释掉的部分表示存在以前包含但当前未保存的其他统计信息或值。
# - ** 返回 **
# - 此方法没有显式返回值。
# 总体而言“stat_save”是对“stat_load”方法的补充允许在处理或分析后将更新的统计数据和值保存回Redis数据库
def stat_reset(self):
self.daily_date = 0 # working date
self.daily_height = 1 # working height, ref. 747376
self.daily_date_string = "" # working date string
self.daily_csupply = 0 # circulating supply
self.daily_sumcsupply = 0 # cumulative circulating supply, for liveliness
self.daily_sumcdd = 0 # cumulative coin days destoryed
self.daily_sumeacdd = 0 # cumulative coin days destoryed(Entity-Adjusted)
self.daily_marketcap = 0 # market capitalization
self.daily_rcap = 0 # Realized capitalization
self.daily_earcap = 0 # Realized capitalization(Entity-Adjusted)
'''
self.daily_lth_marketcap = 0 # Long Term Holder market capitalization
self.daily_lth_rcap = 0 # Long Term Holder Realized capitalization
self.daily_sth_marketcap = 0 # Short Term Holder market capitalization
self.daily_sth_rcap = 0 # Short Term Holder Realized capitalization
'''
#self.daily_uprofit = 0 # Unrealized Profit
#self.daily_uloss = 0 # Unrealized Loss
#self.daily_lthnupl = 0 # Long Term Holder NUPL
#self.daily_sthnupl = 0 # Short Term Holder NUPL
self.stat_daily_reset()
self.daily_rprofit = 0 # realized profit
self.daily_rloss = 0 # realized loss
#v2
self.daily_sumvdd = 0
self.daily_sumdays = 0
# “stat_reset”方法负责将各种每日统计数据和值重置为其初始状态。下面是该方法的每个部分的作用
# - ** 属性重置 **
# - 它将与每日日期、高度和日期字符串相关的属性重置为默认值。
# - ** 统计重置 **
# - 它将与供应、资本化、硬币销毁天数 CDD 和其他指标相关的各种每日统计数据设置为零。
# - 一些统计数据如累计供应量、累计CDD、市值和已实现资本化设置为零表示当天分析的新开始。
# - 某些统计数据,例如长期持有者 LTH 和短期持有者 STH 指标、未实现损益以及LTH和STH的未实现净未实现损益 NUPL被注释掉表明它们在当前实现中未重置或使用。
# - ** 子程序调用 **
# - 它调用“stat_daily_reset”方法此处未定义该方法但可能负责重置特定于应用程序要求的其他每日统计信息。
# - ** v2
# 统计信息重置 **
# - 它重置了与指标相关的其他v2统计信息例如已用产出年龄总和 sumvdd 和已用产出天数总和 sumdays
# 总体而言“stat_reset”为重新计算每日统计数据和指标提供了一个干净的石板确保每天的分析从一致的初始状态开始。
def stat_daily_reset(self):
self.daily_profit = 0 # Number of UTXOs in Profit
self.daily_fees = 0 # block fees each day
self.daily_txs = 0 # block txs exclude coinbase transaction
self.daily_new_address = 0 # number of new address
self.daily_total_address = redisif.get_addr_cnt() # number of address
self.daily_new_address_volume = 0 # volume of new address
self.daily_active_address = 0 # number of active address
self.daily_send_address = 0 # number of send address
self.daily_receive_address = 0 # number of receive address
self.daily_volume = 0 # volume for each day
self.daily_eavolume = 0 # volume for each day(Entity-Adjusted)
self.daily_asol = 0 # Average Spent Output Lifespan
self.daily_eaasol = 0 # Average Spent Output Lifespan(Entity-Adjusted)
self.daily_atxs = 0 # exclude transaction < 1 hour
self.daily_sopr_buy = 0 # Spent Output Profit Ratio for buyin
self.daily_asopr_buy = 0 # Spent Output Profit Ratio(exclude < 1 hour) for buyin
self.daily_easopr_buy = 0 # Spent Output Profit Ratio(Entity-Adjusted) for buyin
self.daily_lthsopr_buy = 0 # Long-Term Holder SOPR for buyin
self.daily_sthsopr_buy = 0 # Short-Term Holder SOPR for buyin
self.daily_sopr_sell = 0 # Spent Output Profit Ratio for sellout
self.daily_asopr_sell = 0 # Spent Output Profit Ratio(exclude < 1 hour) for sellout
self.daily_easopr_sell = 0 # Spent Output Profit Ratio(Entity-Adjusted) for sellout
self.daily_lthsopr_sell = 0 # Long-Term Holder SOPR for sellout
self.daily_sthsopr_sell = 0 # Short-Term Holder SOPR for buyin
self.daily_cdd = 0 # Coin Days Destroyed
self.daily_sacdd = 0 # Supply-Adjusted CDD
self.daily_eacdd = 0 # Coin Days Destroyed(Entity-Adjusted)
self.daily_cdd_days1 = 0 # cdd < 1days
self.daily_cdd_days7 = 0 #
self.daily_cdd_days30 = 0 #
self.daily_cdd_days60 = 0 #
self.daily_cdd_days90 = 0 #
self.daily_cdd_days180 = 0 #
self.daily_cdd_days365 = 0 #
self.daily_cdd_days730 = 0 #
self.daily_mintusd = 0 # daily coin issuance (in USD), for Puell Multiple
self.daily_mvrv = 0 # market-value-to-realized-value ratio
self.daily_lth_mvrv = 0 # Long Term Holder MVRV
self.daily_sth_mvrv = 0 # Short Term Holder MVRV
self.daily_nupl = 0 # Net Unrealized Profit/Loss
self.daily_height_begin = 0
self.daily_height_end = 0
self.daily_price = 0
self.redis.reset_active_address()
self.redis.reset_send_address()
self.redis.reset_receive_address()
#v2
self.daily_mint = 0
self.daily_lth_volume = 0
self.daily_frm = 0
self.daily_cvdd = 0
self.daily_nvt_ratio = 0
self.daily_balanced_price = 0
self.daily_realized_price = 0
self.daily_transferred_price = 0
self.daily_velocity = 0
self.daily_mempool_volume = 0
# “stat_daily_reset”方法将各种每日统计信息和值重置为其初始状态。下面是该方法的每个部分的作用
# - ** 属性重置 **
# - 它将与利润、费用、交易、地址、数量、已用产出寿命、SOPR已用产出利润率、CDD硬币销毁天数、Mint硬币发行、MVRV市场价值与已实现价值比率、NUPL净未实现利润 / 损失)、高度开始、高度结束和价格相关的属性重置为零或初始值。
# - ** 子程序调用 **
# - 它重置Redis数据库中的活动地址、发送地址和接收地址。
# - ** v2统计信息重置 **
# - 它重置了与铸币厂硬币发行量、LTH长期持有者交易量、FRM费用比率倍数、CVDD累计销毁价值天数、NVT网络价值与交易比率比率、平衡价格、实现价格、转移价格、速度和内存池数量等指标相关的其他v2统计数据。
# 总体而言“stat_daily_reset”可确保在每天分析开始时将各种每日统计数据和值重置为初始状态从而为新一天的数据收集和处理提供干净的石板。
def stat_cdd(self, prev_value, days):
cdd = prev_value * days
self.daily_cdd += cdd
self.daily_sumcdd += cdd
if days <= 1:
self.daily_cdd_days1 += cdd
elif days <= 7:
self.daily_cdd_days7 += cdd
elif days <= 30:
self.daily_cdd_days30 += cdd
elif days <= 60:
self.daily_cdd_days60 += cdd
elif days <= 90:
self.daily_cdd_days90 += cdd
elif days <= 180:
self.daily_cdd_days180 += cdd
elif days <= 365:
self.daily_cdd_days365 += cdd
else:
self.daily_cdd_days730 += cdd
# “stat_cdd”方法根据先前的值和天数计算和更新硬币销毁天数 CDD 统计数据。
# - ** 输入参数 **
# - 'prev_value'CDD的上一个值。
# - 'days'计算CDD的天数。
# - ** CDD计算 **
# - 它通过将前一个值乘以天数来计算给定天数的CDD。
# - ** 每日CDD更新 **
# - 它将计算出的CDD添加到“daily_cdd”属性中该属性表示当天的总CDD。
# - 它还将计算出的CDD添加到“daily_sumcdd”属性中该属性表示累积CDD。
# - ** 按时间段更新CDD **
# - 它根据天数对CDD进行分类并根据时间段将其添加到相应的属性“daily_cdd_days1”、“daily_cdd_days7”等中。
# 这种方法有效地跟踪和更新不同时间段的各种CDD统计数据根据硬币的年龄提供对硬币走势的洞察。
def get_price(self, height, dayutc):
price = 0
dayutcstr = str(dayutc)
cnt = 0
while cnt < 3:
cnt += 1
if dayutcstr in self.history_prices:
price = self.history_prices[dayutcstr]
break
elif dayutcstr == str(self.current_utc):
price = self.get_current_price()
self.current_price = price
self.history_prices[dayutcstr] = self.current_price
break
else:
print("failed get price", height, dayutcstr)
self.get_history_price()
self.history_prices = self.get_history_price2()
self.current_price = self.get_current_price()
self.current_utc = self.get_current_utc()
self.history_prices[str(self.current_utc)] = self.current_price
price = self.history_prices[dayutcstr]
break
return price
# “get_price”方法检索给定高度和UTC日的价格。
# - ** 参数 **
# - 'height':块高度。
# - 'dayutc'需要价格的UTC日期。
# - ** 价格检索 **
# - 它首先将“dayutc”转换为字符串。
# - 它尝试在'history_prices'字典属性中找到与'dayutc'对应的价格。
# - 如果找到价格,则返回价格。
# - 如果'dayutc'是当前UTC 日('self.current_utc'),它会使用
# 'get_current_price'方法检索当前价格,并相应地更新'current_price'和'history_prices'属性。
# - 如果在历史记录中找不到价格并且它不是当天它会尝试通过调用“get_history_price”和“get_history_price2”方法来检索历史价格。然后它检索当前价格并像以前一样更新属性。
# - 如果价格检索在三次尝试后失败,它会打印一条消息,指示失败。
# - ** 返回 **
# - 它返回检索到的价格。如果未找到价格则返回0。
# 此方法可确保从历史数据或当前价格中检索价格,从而处理历史数据可能丢失或过时的情况。
def save_db(self, dayutc):
if dayutc != self.daily_date:
print("cmp", dayutc, self.daily_date)
start = time.time()
self.daily_sumcsupply += (self.daily_csupply)
daily_profit_rate = self.daily_profit / self.daily_txs if self.daily_txs != 0 else 0
daily_sopr = self.daily_sopr_sell / self.daily_sopr_buy if self.daily_sopr_buy != 0 else 0
daily_sasopr = self.daily_asopr_sell / self.daily_asopr_buy if self.daily_asopr_buy != 0 else 0
daily_easopr = self.daily_easopr_sell / self.daily_easopr_buy if self.daily_easopr_buy != 0 else 0
daily_lthsopr = self.daily_lthsopr_sell / self.daily_lthsopr_buy if self.daily_lthsopr_buy != 0 else 0
daily_sthsopr = self.daily_sthsopr_sell / self.daily_sthsopr_buy if self.daily_sthsopr_buy != 0 else 0
self.daily_asol = self.daily_asol / self.daily_atxs if self.daily_atxs != 0 else 0
self.daily_eaasol = self.daily_eaasol / self.daily_atxs if self.daily_atxs != 0 else 0
self.daily_sacdd = self.daily_cdd / self.daily_csupply if self.daily_csupply != 0 else 0
self.daily_mvrv = self.daily_marketcap / self.daily_rcap if self.daily_rcap != 0 else 0
liveliness = self.daily_sumcdd / self.daily_sumcsupply if self.daily_sumcsupply != 0 else 0
ealiveliness = self.daily_sumeacdd / self.daily_sumcsupply if self.daily_sumcsupply != 0 else 0
rplrate = self.daily_rprofit - self.daily_rloss
dormancy = self.daily_cdd / self.daily_volume if self.daily_volume != 0 else 0
adormancy = dormancy / self.daily_csupply if self.daily_csupply != 0 else 0
self.daily_eavolume -= (self.daily_fees)
eadormancy = self.daily_eacdd / self.daily_eavolume if self.daily_eavolume != 0 else 0
nupl = (self.daily_marketcap - self.daily_rcap) / self.daily_marketcap if self.daily_marketcap != 0 else 0
self.daily_total_address = redisif.get_addr_cnt() # number of address
self.daily_height_end = self.height - 1 if self.height > self.daily_height_begin else self.daily_height_begin
dbif.update_to_dailyinds(self.daily_date, self.daily_height_begin, self.daily_height_end, daily_profit_rate,
self.daily_fees, self.daily_txs, self.daily_new_address, self.daily_total_address,
self.daily_new_address_volume, self.daily_active_address,
self.daily_send_address, self.daily_receive_address, self.daily_volume,
self.daily_eavolume, daily_sopr, daily_sasopr, daily_easopr, daily_lthsopr,
daily_sthsopr,
self.daily_asol, self.daily_eaasol, dormancy, adormancy, eadormancy,
self.daily_cdd, self.daily_sacdd, self.daily_eacdd, self.daily_cdd_days1,
self.daily_cdd_days7, self.daily_cdd_days30, self.daily_cdd_days60,
self.daily_cdd_days90, self.daily_cdd_days180, self.daily_cdd_days365,
self.daily_cdd_days730, self.daily_csupply, self.daily_mintusd,
self.daily_sumcsupply, self.daily_sumcdd, self.daily_sumeacdd,
liveliness, ealiveliness, self.daily_rprofit, self.daily_rloss, rplrate,
self.daily_price, self.daily_marketcap, self.daily_rcap, self.daily_earcap,
self.daily_mvrv, nupl,self.daily_cdd*self.daily_price)
#v2
#self.daily_sumdays = (dayutc - 1231469665)/3600/24
self.daily_sumdays = self.daily_sumcdd/self.daily_csupply
if self.daily_csupply > 0:
self.daily_realized_price = self.daily_rcap/self.daily_csupply
if self.daily_sumdays > 0:
self.daily_transferred_price = self.daily_sumvdd/(self.daily_sumdays*self.daily_csupply)
self.daily_balanced_price = self.daily_realized_price - self.daily_transferred_price
if self.daily_fees > 0:
self.daily_frm = (self.daily_fees + self.daily_mint)/self.daily_fees
if self.daily_sumdays > 0:
self.daily_cvdd = self.daily_sumvdd/(self.daily_sumdays*6000000)
#daily_vp = self.daily_volume*self.daily_price
#if daily_vp > 0:
if self.daily_volume > 0 and self.daily_price > 0:
self.daily_nvt_ratio = self.daily_marketcap/self.daily_volume/self.daily_price
if self.daily_marketcap > 0:
self.daily_velocity = self.daily_volume*self.daily_price/self.daily_marketcap
dbif.update_to_dailyindsv2(dayutc, self.daily_height_begin, self.daily_height_end,self.daily_lth_volume, self.daily_frm, self.daily_cvdd, self.daily_realized_price, self.daily_transferred_price, self.daily_balanced_price, self.daily_nvt_ratio, self.daily_velocity)
self.stat_daily_reset()
self.daily_date = dayutc
self.daily_height_begin = self.height
print("save_db", f'coast:{time.time() - start:.4f}s')
# “save_db”方法似乎在将数据保存到数据库之前执行多次计算和更新。让我们分解一下它的功能
# - ** 参数 **
# - 'dayutc'保存数据的UTC日期。
# - ** 功能性 **
# - 如果“dayutc”不等于当前“daily_date”则继续进行数据处理。
# - 根据当天收集的数据计算各种指标和比率。
# - 这些指标包括:
# - 每日利润率、支出产出利润率 SOPR、硬币销毁天数 CDD、已实现资本化、市值与已实现价值比率 MVRV、净未实现损益 NUPL 等。
# - 然后使用“dbif.update_to_dailyinds”和“dbif.update_to_dailyindsv2”方法将计算出的指标存储在数据库中。
# - 存储数据后该方法使用“stat_daily_reset”方法重置每日统计信息。
# - 最后它会更新第二天数据收集的“daily_date”和“daily_height_begin”属性。
# - ** 打印声明 **
# - 包含一个打印语句,以指示保存数据所需的持续时间。
# 这种方法本质上是处理每天指标和统计数据的数据处理和存储,确保数据库使用最新信息进行更新。
def stat_block(self, dbif, redisif, config):
self.redis = redisif
self.stat_load(redisif, config)
if self.daily_date is None:
self.stat_reset()
return
print("start height", self.height)
# return
self.height += 1
print("start")
while True:
start = time.time()
blockstats = self.rpc_cmd("getblockstats")
print("getblockstats", f'coast:{time.time()-start:.4f}s')
start = time.time()
#mempoolinfo = self.rpc_cmd("getmempoolinfo")
blockdetail = self.rpc_cmd("getblock")
print("getblock", f'coast:{time.time() - start:.4f}s')
block_start = time.time()
self.blocktime = blockdetail.get_time()
block_time2 = time.gmtime(self.blocktime)
daystr = time.strftime("%d %b %Y", block_time2)
dayutc = int(time.mktime(time.strptime(daystr, "%d %b %Y")))
dayutcstr = str(dayutc)
if self.daily_date == 0:
self.daily_date = dayutc
#print("mempoolinfo", mempoolinfo, mempoolinfo["size"], float(mempoolinfo["total_fee"]))
#time.sleep(10)
#dbif.update_to_realtimeindsv2(self.blocktime, int(mempoolinfo["size"]), float(mempoolinfo["total_fee"]))
#break
self.save_db(dayutc)
blocktxs = blockdetail.get_transactions()
self.height = blockdetail.get_height()
redisif.set_block_time(self.height, self.blocktime) # table for block height and time for later query
mint = blockstats["subsidy"] / 100000000
self.daily_csupply += (mint)
#self.daily_sumcsupply += (self.daily_csupply)
self.daily_mint += (mint)
block_fees = (blockstats["totalfee"] / 100000000)
self.daily_fees += block_fees
self.daily_volume += (blockstats["total_out"] / 100000000)
self.daily_txs += (blockstats["txs"] - 1) # exclude coinbase tx
block_price = self.get_price(self.height, dayutc)
self.daily_mintusd += (block_price * (mint+block_fees))
self.daily_price = block_price
self.daily_marketcap = (self.daily_csupply * block_price)
# genisis_time = redisif.get_block_time(1)
'''genisis_time = 1231006505
days = (self.blocktime - genisis_time) / 3600 / 24
if days >= 155:
self.daily_lth_marketcap += (self.daily_csupply * block_price)
else:
self.daily_sth_marketcap += (self.daily_csupply * block_price)
'''
for tx in blocktxs:
txid = tx.get_txid()
vins = tx.get_vins()
vouts = tx.get_vouts()
vin_hexs = []
vin_addrs = []
vin_values = []
vin_dts = []
vin_volume = 0
vin_volume_change = 0
vin_days_change = 0
vin_cdd = 0
vin_cdd_change = 0
vin_rcap_change = 0
vin_sopr = 0
vin_asopr_diff = 0
vout_change_value = 0
if not tx.is_coinbase():
for vin in vins:
# print(self.height, "vin", vin, type(vin))
if vin.is_prevout():
prevout = vin["prevout"]
prev_height = prevout["height"]
prev_value = float(prevout["value"])
prev_scriptpubkey = prevout["scriptPubKey"]
#prev_type = prev_scriptpubkey["type"]
prev_hex = prev_scriptpubkey["hex"]
prev_address = self.get_vin_address(prev_scriptpubkey, prev_height, txid)
prev_blocktime = redisif.get_block_time(prev_height)
redisif.save_addr(prev_address, -prev_value)
if not redisif.is_send_address(prev_address):
self.daily_send_address += 1
if not redisif.is_active_address(prev_address):
self.daily_active_address += 1
days = (self.blocktime - prev_blocktime) / 3600 / 24
vin_cdd += (prev_value * days)
self.stat_cdd(prev_value, days)
if days >= 155:
self.daily_lth_volume += prev_value
vin_addrs.append(prev_address)
vin_values.append(prev_value)
vin_dts.append(prev_blocktime)
vin_hexs.append(prev_hex)
vin_volume += prev_value
vin_asopr_diff += ((self.blocktime - prev_blocktime) * prev_value)
prevutc = self.get_day_utc(prev_blocktime)
prev_price = self.get_price(prev_height, prevutc)
vin_sopr += (prev_price * prev_value)
self.daily_sumvdd += (prev_value * days * prev_price)
self.daily_rcap -= (prev_price * prev_value)
have_change = False
for vout in vouts:
scriptpubkey = vout.get_script_pubkey()
# vout address is same with vin address
if scriptpubkey["hex"] == prev_scriptpubkey["hex"]:
vin_rcap_change += (prev_value * prev_price)
vin_volume_change += prev_value
vout_change_value = float(vout.get_value())
days = (self.blocktime - prev_blocktime) / 3600 / 24
vin_days_change += days
vin_cdd_change += (prev_value * days)
have_change = True
break
if not have_change:
self.daily_earcap -= (prev_price * prev_value)
self.daily_eacdd += (prev_value * days)
self.daily_eavolume += (vin_volume - vout_change_value)
vin_sopr_change = vin_sopr
#vin_change_price = 0
if vin_rcap_change != 0:
if vin_volume_change != 0:
vin_change_price = vin_rcap_change / vin_volume_change
self.daily_earcap -= (vin_rcap_change - (vin_change_price * vout_change_value))
vin_sopr_change -= (vin_change_price * vout_change_value)
if vin_cdd_change != 0:
if vin_volume_change != 0:
vin_change_days = vin_cdd_change / vin_volume_change
vin_cdd_change -= (vin_change_days * vout_change_value)
self.daily_sumeacdd += (vin_cdd - vin_cdd_change)
self.daily_sopr_buy += vin_sopr
self.daily_easopr_buy += vin_sopr_change
if vin_asopr_diff >= 3600 * vin_volume:
self.daily_asopr_buy += vin_sopr
if vin_volume > 0:
self.daily_asol += (vin_cdd/vin_volume)
self.daily_eaasol += (vin_cdd / vin_volume)
if vin_volume_change > 0:
self.daily_eaasol -= (vin_cdd_change/vin_volume_change)
self.daily_atxs += 1
if vin_asopr_diff >= 3600 * 155 * 24 * vin_volume:
self.daily_lthsopr_buy += vin_sopr
else:
self.daily_sthsopr_buy += vin_sopr
vout_price = block_price
vout_volume = 0
vout_volume_change = 0
vout_sopr = 0
vout_sopr_change = 0
for vout in vouts:
vout_value = float(vout.get_value())
vout_volume += vout_value
scriptpubkey = vout.get_script_pubkey()
vout_type = scriptpubkey["type"]
vout_address = self.get_vout_address(scriptpubkey, self.height, txid)
vout_hex = scriptpubkey["hex"]
if not redisif.is_in_addr(vout_address):
self.daily_new_address_volume += vout_value
self.daily_new_address += 1
redisif.save_addr(vout_address, vout_value)
if not redisif.is_receive_address(vout_address):
self.daily_receive_address += 1
if not redisif.is_active_address(vout_address):
self.daily_active_address += 1
self.daily_rcap += (vout_price * vout_value)
vout_sopr += (vout_price * vout_value)
have_change = False
for cmp in vin_hexs:
if cmp == vout_hex:
vout_volume_change += vout_value
have_change = True
break
if not have_change:
self.daily_earcap += (vout_price * vout_value)
vout_sopr_change += (vout_price * vout_value)
if self.height > 787556:
if (vout_price * vout_value) >= self.rules["flag_big_vout"]:
if vin_volume != 0:
days = vin_cdd / vin_volume
buyin = vin_sopr / vin_volume
sellout = vout_price
profit = 0
if buyin != 0:
profit = (sellout - buyin) / buyin
dbif.update_to_bigamountvout(self.blocktime, txid, \
vout_address, vout.get_n(), vout_type, \
vout_value, self.height, days, buyin, sellout, profit)
self.daily_easopr_sell += vout_sopr_change
self.daily_sopr_sell += vout_sopr
if vin_asopr_diff > 3600 * vin_volume:
self.daily_asopr_sell += vout_sopr
if vin_asopr_diff >= 3600 * 155 * 24 * vin_volume:
self.daily_lthsopr_sell += vout_sopr
else:
self.daily_sthsopr_sell += vout_sopr
if vin_volume != 0:
if block_price > (vin_sopr / vin_volume):
self.daily_rprofit += (vout_sopr - vin_sopr)
if block_price < (vin_sopr / vin_volume):
self.daily_rloss += (vin_sopr - vout_sopr)
buyin = vin_sopr / vin_volume
sellout = vout_sopr / vout_volume if vout_volume != 0 else 0
if sellout > buyin:
self.daily_profit += 1
else:
for vout in vouts:
vout_value = float(vout.get_value())
scriptpubkey = vout.get_script_pubkey()
vout_address = self.get_vout_address(scriptpubkey, self.height, txid)
vout_price = block_price
self.daily_rcap += (vout_price * vout_value)
self.daily_earcap += (vout_price * vout_value)
if not redisif.is_in_addr(vout_address):
self.daily_new_address_volume += vout_value
self.daily_new_address += 1
redisif.save_addr(vout_address, vout_value)
if not redisif.is_receive_address(vout_address):
self.daily_receive_address += 1
if not redisif.is_active_address(vout_address):
self.daily_active_address += 1
self.stat_save(redisif)
print("statblock", f'coast:{time.time() - block_start:.4f}s')
start = time.time()
self.rpc_cmd("getblockcount")
print("getblockcount", f'coast:{time.time() - start:.4f}s')
# 这个“stat_block”方法似乎可以处理每个块的统计计算和数据库更新。让我们分解一下它的功能
# - ** 参数 **
# - 'dbif':数据库接口对象。
# - 'redisif'Redis接口对象。
# - 'config':配置对象。
# - ** 功能性 **
# - 它使用“stat_load”方法从Redis加载统计信息。
# - 如果'daily_date'为None则使用'stat_reset'重置统计信息并返回。
# - 它使用RPC命令'getblockstats'、'getblock')检索区块统计信息和详细信息。
# - 它计算各种指标并针对当前区块更新它们:
# - 每日供应量('daily_csupply')、费用('daily_fees')、交易量('daily_volume')、交易数量('daily_txs')等。
# - 它检索区块时间并计算UTC日。
# - 它使用“save_db”方法保存每日数据。
# - 它处理区块中的每笔交易:
# - 更新地址、数量、支出产出利润率 SOPR、硬币销毁天数 CDD 等。
# - 它使用“stat_save”方法将统计数据保存回Redis。
# - ** 打印报表 **
# - 包含print语句以指示各种操作'getblockstats'、'getblock'、'getblockcount')所花费的持续时间。
# 总体而言,此方法处理每个块的统计信息的收集和处理,确保数据库使用最新信息进行更新。
def init_config(filename):
fconfig = open(filename)
config = ujson.load(fconfig)
fconfig.close()
dbif = db_if_qt.DbIf(host="172.17.0.1", port=4419, user="root", password="IeQcJNnagkaFP1Or", dbname="btcdb")
redisif = redis_if_qt.RedisIf(host="127.0.0.1", port=6379, password="", db=0)
return dbif, redisif, config
# “init_config”函数通过从 JSON 文件加载配置来初始化配置,然后根据 JSON 中提供的配置创建数据库和 Redis 接口的实例。下面是一个细分:
# -**参数**
# - 'filename'JSON配置文件的名称。
# -**功能性**
# 1. **打开JSON配置文件**
# - 在读取模式下打开指定的 JSON 配置文件“filename”
# - 使用“ujson.load”函数从文件加载 JSON 数据。
# - 关闭文件。
# 2. **创建数据库和Redis接口**
# - 使用 'db_if_qt.DbIf“其中包含从加载的配置中获取的主机、端口、用户、密码和数据库名称等参数。
# - 使用“redis_if_qt”初始化 Redis 接口 'redisif'。RedisIf“其中包含从加载的配置中获取的主机、端口、密码和数据库等参数。
# 3. **返回**
# - 返回初始化的数据库接口('dbif'、Redis 接口('redisif')和加载的配置。
# - **返回值**
# - 'dbif':初始化的数据库接口对象。
# - 'redisif':初始化的 Redis 接口对象。
# - 'config':将配置加载为 Python 字典。
# 该函数封装了加载配置设置以及初始化数据库和 Redis 接口的过程,为应用程序提供了一种方便的环境设置方式。
if __name__ == '__main__':
dbif, redisif, config = init_config("btcstat_qt.conf")
#print("init_config")
#redisif.reset_btc_data()
statif = StatIf()
#print("StatIf")
statif.stat_block(dbif, redisif, config)
# 此代码块是脚本的入口点。让我一步一步地解释一下:
# - 'if __name__ == '__main__'':此行确保仅当脚本直接运行时才执行以下代码块,而不是将其作为模块导入到另一个脚本中。
# - 'dbif redisif config = init_config“btcstat_qt.conf”':调用 'init_config' 函数初始化数据库接口 'dbif'、Redis 接口 'redisif'),并将配置设置加载到 'config' 变量中。这些对象是进一步操作所必需的。
# - 'statif = StatIf'它创建“StatIf”类的实例。这表明在代码的其他地方定义了一个名为“StatIf”的类。
# - 'statif.stat_blockdbif redisif config':调用 'StatIf' 实例的 'stat_block' 方法,传递数据库接口 'dbif'、Redis 接口 'redisif' 和加载的配置 'config')。此方法可能使用提供的接口和配置执行一些与区块链数据相关的统计操作。
# 总体而言,此脚本初始化必要的组件,例如数据库和 Redis 接口加载配置设置然后使用“StatIf”类执行统计操作。