# 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.gmtime（time.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.loads（response_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”函数在00：00：00时将给定的UTC时间戳转换为相应的UTC日期（自Unix纪元以来的秒数）。以下是其功能的细分：
    # - ** 参数 **：
    # - 'utc_time'：表示要转换的UTC时间戳的整数。
    # - ** 功能性 **：
    # - 它使用“time.gmtime（utc_time）”将提供的UTC时间戳转换为UTC时区的时间元组。
    # - 然后，它使用'time.strftime（“%d %b %Y”， t）'将UTC时间元组格式化为表示日期的字符串，格式为“DDMonYYYY”（例如，“01Jan2024”）。
    # - 最后，它使用'time.mktime（time.strptime（daystr， “%d %b %Y”））'将格式化的日期字符串转换回UTC时间戳。
    # - ** 返回 **：
    # - 该函数返回一个整数，表示所提供日期在00：00：00小时的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_stats（self.height）'检索当前块高度 （'self.height'） 的块统计信息。
    # - 如果'cmd'为 “getblock”，则使用'rpc.blockchain.get_block（self.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_block（dbif， redisif， config）'：调用 'StatIf' 实例的 'stat_block' 方法，传递数据库接口 （'dbif'）、Redis 接口 （'redisif'） 和加载的配置 （'config'）。此方法可能使用提供的接口和配置执行一些与区块链数据相关的统计操作。
# 总体而言，此脚本初始化必要的组件，例如数据库和 Redis 接口，加载配置设置，然后使用“StatIf”类执行统计操作。
