# 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 btc24h_db_if
import btc24h_redis_if
import pymysql
DEF_CONFIG_RULES = "rules"
oklinkheaders = {'Ok-Access-Key': "6b7bb4fb-70d5-44a4-bc6f-0a43a8e39896"}
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”的类，该类似乎与使用RPC（远程过程调用）与比特币节点交互有关。此类使用用于连接到比特币节点的IP地址、端口、用户名和密码的参数进行初始化。
    # 以下是正在发生的事情的简要细分：
    # - '__init__'方法：这是Python类中的一种特殊方法，在创建类的实例时调用。在此方法中，实例变量“self.host”、“self.port”、“self.user”和“self.pwd”使用提供的值进行初始化。这些变量似乎存储了比特币节点的连接信息。
    # - 'self.rpc'：此变量初始化为'None'。它似乎旨在保存RPC客户端的实例（可能来自“easybitcoinrpc.RPC' 类）。
    # - 'self.height'：此变量初始化为'0'。它似乎旨在存储当前的区块链高度。
    # - 'self.pricedict'：此字典初始化为空字典 （'{}'）。从这个片段中并不能立即看出它的目的，但它似乎与存储与价格相关的信息有关。
    # - 'setup（'mainnet'）'：这个函数调用似乎设置了一些与比特币网络相关的配置，特别是针对主网的配置。
    # 总的来说，这个类看起来像是作为一个接口（'StatIf'），用于与比特币节点进行交互，以获取与区块链相关的统计数据或其他信息。
    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
    # 这种“get_vin_address”方法似乎用于从事务输入中提取以前的输出地址。
    # 以下是正在发生的事情的细分：
    # - 该方法采用三个参数：“prev_scriptpubkey”、“prev_height”和“txid”。这些似乎分别表示上一个输出的scriptPubKey、包含交易的块的高度和交易ID。
    # - 它首先检查上一个scriptPubKey的类型。如果它不是“nulldata”（意味着它是有效的scriptPubKey），它将继续提取地址。
    # - 它检查“prev_scriptpubkey”是否为字典并包含键“地址”。如果是这样，它会从中提取地址。
    # - 如果地址仍为“None”，则进一步检查scriptPubKey是否为公钥。如果是，它会尝试对其进行解码以获取地址。如果解码失败，则会打印错误消息。
    # - 如果地址仍为“None”，则将scriptPubKey的十六进制值分配给“prev_address”。
    # - 最后，它返回“prev_address”。
    # 此方法似乎可以处理从scriptPubKey中提取地址的各种方案，包括处理类型为“pubkey”的scriptPubKeys，并在所有其他尝试失败时回退到使用十六进制值。
    def get_vout_address(self, scriptpubkey, height, txid):
        return self.get_vin_address(scriptpubkey, height, txid)
    # 似乎“get_vout_address”方法只是使用提供的参数调用“get_vin_address”方法。
    # 以下是正在发生的事情的细分：
    # - 'get_vout_address'方法采用与'get_vin_address'相同的参数：'scriptpubkey'、'height'和'txid'。
    # - 它没有实现自己的逻辑来从“scriptpubkey”中提取地址，而是直接调用具有相同参数的“get_vin_address”方法。
    # - 这意味着'get_vout_address'实质上将提取地址的任务委托给'get_vin_address'方法。
    def get_history_price(self, batch_size=5000):
        #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”方法似乎是从Web API获取历史比特币价格数据，并用检索到的数据填充“self.pricedict”字典。
    # 以下是正在发生的事情的细分：
    # - 该方法向指定的URL （'https://data.nasdaq.com/api/v3/datasets/BCHAIN/MKPRU.json?api_key=FZqXog4sR-b7cYnXcRVV'） 发送GET请求以获取历史比特币价格数据。此请求是使用
    # 'requests.get（）'函数发出的。
    # - 然后检查响应状态代码是否为“200”，表示响应成功。
    # - 如果响应成功，它将使用'ujson.loads（）'将响应内容解析为JSON。
    # - 然后，它会检查解析后的JSON是否包含名为“dataset”的键。如果是这样，它将从数据集中检索“数据”。
    # - 它遍历“数据”中的每个条目，并提取日期（“daystr”）和价格（“p”）。它使用'time.mktime（）'将日期转换为UNIX时间戳（'dayutc'）。
    # - 然后，它使用日期作为键（转换为整数）和price作为值来填充“self.pricedict”字典。
    # - 最后，如果它包含数据，它会返回'self.pricedict'。
    # - 该方法在处理后关闭响应对象。
    # 此方法本质上是从提供的API端点获取历史比特币价格数据，对其进行处理，并将其存储在“self.pricedict”字典中以供以后使用。
    def get_history_price2(self, batch_size=5000):
        #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_price”方法相比，这种“get_history_price2”方法似乎是另一种从不同的Web API获取历史比特币价格数据的方法。
    # 以下是此方法中发生的情况：
    # - 该方法首先使用'time.gmtime（）'获取当前UTC时间，然后对其进行格式化以检索当前年份。
    # - 它根据当年构建一个URL来获取比特币价格数据。该URL可以是当前年份，也可以是特定年份范围。
    # - 它为请求设置标头，特别是Messari API密钥。
    # - 它使用'requests.get（）'向构造的URL发送GET请求。
    # - 如果响应状态码为200（表示成功），则将响应内容解析为JSON。
    # - 它检查JSON是否包含名为“data”的键，并从中提取“值”。
    # - 它遍历“values”中的每个条目，并提取时间戳（'dayutc'）和价格（'s'）。它将时间戳转换为人类可读的日期字符串 （'ret_daystr'）
    # - 然后转换回UNIX时间戳 （'ret_dayutc'）。
    # - 它填充“self.pricedict”字典，将日期作为键（转换为字符串），将价格作为值。
    # - 此过程每年都会重复，逐年递减，直到没有更多数据可供获取。
    # - 在收集完所有数据后，它会打印“self.pricedict”并返回它。
    # 此方法有效地从Messari API中检索历史比特币价格数据，并将其存储在'self.pricedict'字典中。
    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） 中的当前时间并将其转换为UNIX时间戳。
    # 以下是此方法中发生的情况的细分：
    # - 它首先使用'time.gmtime（time.time（））'获取UTC中的当前时间，它返回一个表示当前UTC时间的时间结构。
    # - 它使用'time.strftime（“%d %b %Y”， curtime）'
    # 将UTC时间格式化为人类可读的日期字符串。这将以“DD Mon YYYY”格式提供当前日期的字符串表示形式（例如，“01Jan2024”）。
    # - 它使用'time.mktime（time.strptime（daystr， “%d %b %Y”））'将人类可读的日期字符串转换回UNIX时间戳。这会将格式化的日期字符串转换为时间结构，
    # 然后'time.mktime（）'将其转换为UNIX时间戳。
    # - 它返回表示当前UTC时间的UNIX时间戳。
    # 总之，此方法提供了一种将UTC中的当前时间作为UNIX时间戳获取的方法。
    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
    # “get_current_price”方法似乎是另一种实用方法，这次用于从指定的API端点检索以美元为单位的比特币的当前价格。
    # 以下是此方法中发生的情况：
    # - 它向指定的URL （'https://bitcoinexplorer.org/api/price'） 发送GET请求以获取当前比特币价格。此请求是使用'requests.get（）'函数发出的。
    # - 检查响应状态码是否为“200”，表示响应成功。
    # - 如果响应成功，它会使用'ujson.loads（response_price.text）'将响应文本解析为JSON。
    # - 它从解析的JSON响应中提取以美元为单位的价格，并将其转换为浮点数。
    # - 它打印响应文本和提取的价格以进行调试。
    # - 它在处理后关闭响应对象。
    # - 最后，它返回检索到的价格。如果请求失败或无法提取价格，则返回“0”。
    # 总体而言，此方法从指定的API端点获取当前比特币价格，并将其作为浮点数返回。
    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”方法似乎是一种实用方法，用于将给定的UNIX时间戳转换为由该时间戳表示的UTC日（00：00：00 UTC）的开始。
    # 此方法的作用如下：
    # - 它采用UNIX时间戳“utc_time”作为输入。
    # - 它使用'time.gmtime（utc_time）'将给定的时间戳转换为UTC中的时间结构。
    # - 它将UTC时间格式化为人类可读的日期字符串，表示使用'time.strftime（“%d %b %Y”， t）'。此格式为“DD Mon YYYY”（例如，“01 Jan 2024”）。
    # - 它使用'time.mktime（time.strptime（daystr， “%d %b %Y”））'将格式化的日期字符串转换回表示当天开始的UNIX 时间戳。这有效地从时间戳中删除了时间分量，将其设置为午夜 （00：00：00）。
    # - 它返回生成的UNIX时间戳，表示与输入时间戳对应的UTC日期的开始。
    # 总之，此方法提供了一种获取UNIX时间戳的方法，该时间戳表示给定时间戳的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:
                        print("time sleep start")
                        time.sleep(30)
                        print("time sleep end")
                    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（远程过程调用）命令。
    # 此方法的作用如下：
    # - 它首先检查RPC客户端 （'self.rpc'） 是否已初始化。如果没有，它将使用提供的主机、端口、用户名和密码对其进行初始化。
    # - 它进入一个循环，在该循环中，它不断尝试执行指定的RPC命令 （'cmd'），直到它成功。
    # - 在循环内部，它根据'cmd'的值具有不同的分支。根据命令的不同，它使用初始化的RPC客户端 （'self.rpc'） 执行相应的RPC调用。
    # - 如果命令是 “getblockstats”，它会调用具有当前高度的'self.rpc.blockchain.get_block_stats（）'并返回结果。
    # - 如果命令是 “getblock”，它会调用'self.rpc.blockchain.get_block（）'，当前高度和详细程度级别为3（完整块详细信息）并返回结果。
    # - 如果命令是 “getblockcount”，则调用'self.rpc.blockchain.get_block_count（）'来获取当前块计数。如果当前高度与块计数匹配，则等待30秒，然后再次检查。否则，它将递增高度并返回“无”。
    # - 如果命令是 “getmempoolinfo”，它将调用'self.rpc.blockchain.get_mempool_info（）'并返回结果。
    # - 如果该命令与任何指定的命令不匹配，则返回“None”。
    # - 如果在执行RPC命令期间发生任何异常，它会捕获它们，等待1秒钟，然后重新初始化RPC客户端以从潜在的连接问题中恢复。
    # 这种方法提供了一种灵活的方式，可以使用各种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”字典中检索配置数据。
    # - 它使用键“DEF_CONFIG_RULES”从配置中检索规则，并将其分配给“self.rules”。
    # - 它使用“get_history_price”和“get_history_price2”方法加载历史价格数据，并将其分配给“self.history_prices”。
    # - 它使用“get_current_price”方法检索当前价格并将其分配给“self.current_price”。
    # - 它使用“get_current_utc”方法检索当前UTC时间戳并将其分配给“self.current_utc”。
    # - 它将当前价格添加到当前UTC时间戳下的历史价格数据中。
    # - 它从Redis加载各种其他统计数据，并将它们分配给对象的相应属性。
    # - 代码中有一些被注释掉的部分，这些部分似乎与可以从Redis加载的其他统计数据相关，但它们目前处于非活动状态。
    # 此方法实质上使用从Redis检索到的各种统计数据、历史价格数据和当前价格数据来初始化对象。
    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接口对象。
    # - 它使用“redisif.set_btc_data”方法将各种数据保存到Redis。保存的数据包括每日日期、高度、日期字符串、利润、费用、交易数量（“txs”）、活动地址、交易量以及与比特币统计数据相关的各种其他指标。
    # - 代码中有一些被注释掉的部分，这些部分似乎与可以保存到Redis的其他统计数据有关，但它们目前处于活动状态。
    # - 它还保存了一些与版本2 （'v2'） 统计数据相关的数据，例如每日铸币量、长期持有量和自由流通市值。
    # 总的来说，这种方法提供了一种将与比特币相关的统计数据保存到Redis数据库以供以后检索和分析的方法。
    def stat_reset(self):
        self.daily_date = 0  # working date
        self.daily_height = 878280 # 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”方法用于将与统计数据相关的各种属性重置为其初始值。以下是此方法的作用的细分：
    # - 它将“daily_date”、“daily_height”和“daily_date_string”属性设置为其初始值。这些属性分别表示工作日期、工作高度和工作日期字符串。
    # - 它重置了与统计指标相关的几个属性，例如流通供应量、累计流通供应量、累计销毁币天数、市值、已实现资本化、已实现利润等。这些指标似乎与分析比特币持有者的行为有关。
    # - 代码中有一些注释掉的部分似乎与其他统计指标有关，但它们目前处于活动状态。
    # - 它将另一个方法称为“stat_daily_reset”，该方法可能会重置额外的每日统计信息。但是，此处未提供此方法的实现。
    # - 有一些与版本2 （'v2'） 统计信息相关的注释部分，但它们目前不处于活动状态。
    # 总体而言，此方法提供了一种将各种统计属性重置为其初始值的方法，这对于初始化对象的状态或将其重置为已知状态非常有用。
    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”方法用于将各种每日统计指标重置为其初始值。以下是此方法的作用的细分：
    # - 它重置了与每日统计指标相关的各种属性。这些指标包括每日利润、费用、交易数量 （“txs”）、活动地址、交易量、平均花费的输出寿命、花费的输出利润率、销毁的硬币天数等。
    # - 它重置与版本2 （'v2'） 统计数据相关的属性，例如每日铸币量、长期持有量和自由流通市值。
    # - 它会重置其他一些属性，例如“daily_height_begin”和“daily_height_end”。
    # - 它重置了Redis接口对象 （'redis'） 的一些属性，可能与活动地址、发送地址和接收地址有关。
    # 总而言之，这种方法提供了一种将各种每日统计指标重置为其初始值的方法，这对于日常数据分析或重置对象状态时非常有用。
    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”方法根据提供的“prev_value”（可能代表上一个计算周期中的指标值）和“天数”来计算和更新与硬币销毁天数 （CDD） 相关的各种指标。
    # 以下是此方法的作用的细分：
    # - 它通过将“prev_value”乘以“天”的天数来计算CDD值。
    # - 它通过向“daily_cdd”属性添加计算的CDD值来更新该属性。
    # - 它将CDD值分类为不同的时间范围（“天”），并根据时间范围的长度更新相应的属性（“daily_cdd_days1”、“daily_cdd_days7”等）。例如：
    # - 如果“days”小于或等于1，则更新“daily_cdd_days1”。
    # - 如果“天”介于2到7之间，它会更新“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
                if dayutcstr in self.history_prices:
                    price = self.history_prices[dayutcstr]
                else:
                    price=0
                break

        return price
    # “get_price”方法旨在检索特定高度和UTC日期的比特币价格。其工作原理如下：
    # 1.它将“price”变量初始化为0。
    # 2.它将“dayutc”参数（代表UTC日）转换为字符串格式（“dayutcstr”），用作访问历史价格的键。
    # 3.它进入一个最多运行3次的循环，如果价格检索失败，允许重试。
    # 4.循环内部：
    # - 它检查“dayutcstr”键是否存在于“history_prices”字典中。如果是这样，它会将相应的价格分配给“price”变量并跳出循环。
    # - 如果'dayutcstr'等于当前UTC时间 （'self.current_utc'），它将使用'get_current_price'方法检索当前价格，更新当前价格属性 （'self.current_price'）
    # 并将价格存储在当前UTC密钥下的'history_prices'字典中。然后，它将当前价格分配给“price”变量并跳出循环。
    # - 如果上述条件均不满足，则表示无法检索价格，打印失败消息以进行调试，并尝试通过调用“get_history_price”和“get_history_price2”方法更新历史价格。
    # 它还检索当前价格和UTC时间。更新历史价格和当前价格属性后，它会尝试使用“dayutcstr”键再次检索价格。
    # 5.最后，它返回检索到的价格。
    # 这种方法确保从历史数据或在数据不可用时获取当前价格来获得特定高度和日期的比特币价格。它还允许在检索失败时重试和更新数据。
    def save_db(self, dayutc, blocktime):
        #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(blocktime, self.daily_height_begin, self.daily_height_end, daily_profit_rate,
                                     self.daily_fees, self.daily_txs, 0, 0,
                                     0, 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, 0, eadormancy,
                                     self.daily_cdd, 0, 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, 0, 0,
                                     0, 0, 0,
                                     0, 0, 0, 0, 0,
                                     self.daily_price, 0, 0, 0,
                                     0, 0)

            #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(blocktime, self.daily_height_begin, self.daily_height_end,self.daily_lth_volume, self.daily_frm, 0, 0, 0, 0, 0, 0)

            #if dayutc != self.daily_date:
                #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”方法似乎负责将各种统计信息保存到数据库中。让我们分解一下它的功能：
    # 1.该方法首先根据收集到的每日数据计算各种指标。这些指标包括：
    # - 'daily_profit_rate'：利润与交易数量的比率。
    # - “daily_sopr”：售罄交易的已用产出利润率。
    # - 'daily_sasopr'：售罄交易的已用产出利润率（不包括1小时内的交易）。
    # - “daily_easopr”：出售交易的实体调整的已用产出利润率。
    # - “daily_lthsopr”和“daily_sthsopr”：用于买卖交易的长期持有者和短期持有者SOPR。
    # - “休眠”：销毁的硬币天数与每日交易量的比率。
    # - “休眠”：实体调整后的休眠，计算方法与休眠类似，但使用实体调整的销毁天数和数量。
    # - 还计算了其他指标，例如“daily_eavolume”、“daily_height_end”等。
    # 2.然后，该方法调用数据库接口 （'dbif'） 以使用“update_to_dailyinds”和“update_to_dailyindsv2”方法使用计算的指标更新数据库。
    # 3.更新数据库后，该方法会打印出调试信息，包括“dayutc”与“self.daily_date”的比较以及数据库更新所需的时间。
    # 4.该方法不包括重置每日统计数据或更新“daily_date”属性的逻辑。这可能旨在单独处理，也可以作为其他方法的一部分进行处理。
    # 总体而言，“save_db”方法在将每日统计数据保存到数据库以供进一步分析和报告方面起着至关重要的作用。
    def stat_height_time(self, redisif):
        tmp_height = 1
        if self.rpc is None:
            self.rpc = RPC(self.host, self.port, self.user, self.pwd)
        while True:
            blockstats = self.rpc.blockchain.get_block_stats(tmp_height)
            blocktime = blockstats["time"]
            redisif.set_block_time(tmp_height, blocktime)
            print("cache block height, time", tmp_height, blocktime)
            getblockcount = self.rpc.blockchain.get_block_count()
            if tmp_height == getblockcount:
                break
            else:
                tmp_height += 1
    # “stat_height_time”方法似乎负责缓存块高度和相应的时间戳。让我们来看看它的功能：
    # 1.它初始化一个值为1的临时高度变量“tmp_height”。
    # 2.它检查RPC客户端 （'self.rpc'） 是否已初始化。如果没有，它将使用提供的主机、端口、用户名和密码初始化RPC客户端。
    # 3.它进入一个循环，从“tmp_height”开始连续获取每个区块高度的区块统计信息。
    # 4.在循环中：
    # - 它使用RPC客户端的“blockchain.get_block_stats”方法检索当前“tmp_height”的块统计信息。
    # - 它从区块统计信息中提取区块时间。
    # - 它使用提供的“redisif”接口缓存相应区块高度的区块时间。
    # - 它打印出调试信息，指示缓存的块高度及其时间。
    # 5.缓存块时间后，它使用RPC客户端的'blockchain.get_block_count'方法检查'tmp_height'是否已达到最新的块高度 （'getblockcount'）。
    # 6.如果“tmp_height”等于最新的块高度，则循环中断;否则，它会将“tmp_height”递增 1并继续获取块统计信息。
    # 综上所述，该方法持续从区块链获取区块统计信息，使用Redis 缓存每个区块高度的区块时间，并在达到最新的区块高度时停止。此过程可确保有效存储块高度和相应的时间戳，以备将来参考或分析。
    def stat_block(self, dbif, redisif, config):
        self.redis = redisif
        # self.stat_height_time(redisif)
        self.stat_load(redisif, config)
        if self.daily_date is None:
            self.stat_reset()
            #return

        self.height = self.daily_height;

        self.height += 1
        self.daily_height_begin = self.height
        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)
            if dayutc != self.daily_date:
                self.stat_daily_reset()
                self.daily_date = dayutc
                self.daily_height_begin = self.height


            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
                                        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.save_db(dayutc, self.blocktime)
            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”方法似乎负责处理和分析每个区块的区块链数据。让我们分解一下它的功能：
            # 1. ** 初始化 **：
            # - 该方法初始化Redis接口 （'redisif'），并使用'stat_load'方法从Redis加载统计信息。
            # 2. ** 区块高度初始化 **：
            # - 如果'daily_date'为'None'，则使用'stat_reset'方法重置统计信息。
            # 3. ** 块处理循环 **：
            # - 该方法进入一个循环，按顺序处理每个块。
            # - 对于每个区块：
            # - 它使用 RPC命令（'getblockstats'和'getblock'）获取区块统计信息和详细信息。
            # - 它计算各种指标，例如费用、数量和价格。
            # - 它更新统计数据，例如每日费用、交易量和各种SOPR（已用产出利润率）值。
            # - 它使用“save_db”方法将每日统计数据保存到数据库中。
            # - 它使用“stat_save”方法将更新的统计信息保存到Redis。
            # 4. ** 区块高度更新 **：
            # - 处理每个块后，它使用“getblockcount”RPC命令更新块高度。
            # 总体而言，这种方法协调了区块链数据的检索、处理和存储，确保各种统计数据和指标得到正确更新和保存，以供进一步分析。
def init_config(filename):
    fconfig = open(filename)
    config = ujson.load(fconfig)
    fconfig.close()
    dbif = btc24h_db_if.DbIf(host="172.17.0.1", port=4419, user="root", password="IeQcJNnagkaFP1Or", dbname="btcdb")
    redisif = btc24h_redis_if.RedisIf(host="127.0.0.1", port=6379, password="", db=0)
    return dbif, redisif, config
    # “init_config”函数通过从“filename”指定的 JSON 文件加载配置设置来初始化配置设置。然后，它使用配置文件中定义的参数创建数据库 （'dbif'） 和 Redis （'redisif'） 接口的实例。
    # 以下是它的作用的细分：
    # 1. **打开并加载配置文件**：
    #  - 它打开指定的文件（“filename”）。
    #  - 它使用“ujson.load”从文件中读取 JSON 内容。
    #  - 读取后关闭文件。
    # 2. **创建数据库和 Redis 接口**：
    #  - 它使用从配置文件中提取的参数创建数据库接口（'dbif'）的实例。
    #  - 它使用从配置文件中提取的参数创建 Redis 接口 （'redisif'） 的实例。
    # 3. 返回数据库接口、Redis 接口和配置：
    #  - 它返回创建的 'dbif'、'redisif' 和 'config' 对象以供进一步使用。
    # 此函数封装了初始化配置设置以及创建数据库和 Redis 接口的过程，从而可以更轻松地在应用程序中管理这些资源。
if __name__ == '__main__':
    dbif, redisif, config = init_config("btcstat.conf")
    #print("init_config")
    redisif.reset_btc_data()
    statif = StatIf()
    #print("StatIf")
    statif.stat_block(dbif, redisif, config)
    # 1. ** 检查脚本是否为主模块 **：
    # - 'if __name__ == '__main__'：'块确保以下代码仅在直接执行脚本时运行，而不是在将其作为模块导入到另一个脚本中时运行。
    # 2. ** 初始化配置 **：
    # - 它调用带有参数'“btcstat.conf”'的'init_config'函数，以从指定文件加载配置设置。
    # - 它将返回的对象（'dbif'、'redisif'和'config'）分配给变量。
    # 3.实例化“StatIf”对象：
    # - 它创建“StatIf”类的实例，并将其分配给变量“statif”。
    # 4. ** 调用“stat_block”方法 **：
    # - 它调用'statif'对象的'stat_block'方法，将数据库接口 （'dbif'）、Redis接口 （'redisif'） 和配置 （'config'） 作为参数传递。
    # - 鉴于所涉及的方法和对象的名称，此方法可能会启动监控和处理区块链数据的过程。
    # 5. ** 执行流程 **：
    # - 执行从'if __name__ == '__main__'：'块开始。
    # - 配置已初始化。
    # - 创建“StatIf”的实例。
    # - 调用“stat_block”方法，该方法有望处理区块链数据处理。
