from dingtalkchatbot.chatbot import DingtalkChatbot from datetime import datetime, timedelta import pandas as pd import numpy as np import ccxt import time import sys import os #QQ747554421 apiKey = '' secret = '' binance = ccxt.binance({"apiKey": apiKey, 'secret': secret}) def fetch_klines(): # 获取K线数据 data = binance.fetch_ohlcv('BTC/USDT', timeframe='30m', limit=300) # 转化数据 df = pd.DataFrame({}) df['date'] = [x[0] for x in data] df['open'] = [x[1] for x in data] df['high'] = [x[2] for x in data] df['low'] = [x[3] for x in data] df['close'] = [x[4] for x in data] df['volume'] = [x[5] for x in data] df['date'] = pd.to_datetime(df['date'], unit='ms') # UTC 0时区的时间,格林威治的时间。 df['date'] = df['date'] + pd.Timedelta(hours=8) # 计算开平仓信号 df = signal_vix(df) return df def buy_in(): # 买入建仓 # 获取当前账户资金量 total_all = binance.fetch_balance() # 开仓金额为账户总额的1%,按照最大亏损风险7.61%进行计算 position = float(total_all['USDT']['total']) * 0.01 / 0.0761 # 获取最新价格 last = float(binance.fetch_ticker('BTC/USDT')['last']) result_buy = {} try: # 为了避免买入失败,直接采用现价多2%的价格一次性买入,后续资金量大时,需要优化买入流程 result_buy = binance.create_order(symbol='BTC/USDT', type='limit', side='buy', amount=position / last, price=last * 1.02) except Exception as e: print(5) print(e) b = '错误原因为:%s' % e fengexian = '-' * 50 # 记录时的分隔线 # 获取当前时间 df_end = datetime.now() dt = df_end.strftime('%Y-%m-%d %H:%M:%S') dt1 = ['%s' % b + '\n' + '报错时间:%s' % dt + '\n' + '错误位置:1' + '\n' + '%s' % fengexian] log(dt1) print("程序报错准备返回主程序重启") message = "买入建仓失败,账户余额不足。" bot_message(dt, message) dingdanhao = result_buy['id'] # 等待5秒后查询订单,防止查询不到订单的情况 time.sleep(5) df1 = {} try: df1 = binance.fetch_order(dingdanhao, 'BTC/USDT') except: i_order = 0 while i_order < 30: # 正常为10,更改成10分钟为20 # 如果在前5秒钟内查询不到订单,就每隔30秒查询一次,连续查询5分钟 time.sleep(30) try: df1 = binance.fetch_order(dingdanhao, 'BTC/USDT') if df1: i_order = 41 except: pass i_order += 1 if not df1: df1 = binance.fetch_order(dingdanhao, 'BTC/USDT') if df1['status'] == 'closed' and df1['side'] == 'buy': # 订单状态 'open', 'closed', 'canceled' # 记录成交数据 # 获取当前时间 df_end = datetime.now() dt = df_end.strftime('%Y-%m-%d %H:%M:%S') # 增加订单成交时间 dt_deal = pd.to_datetime(df1['info']['updateTime'], unit='ms') # UTC 0时区的时间,格林威治的时间。 dt_deal = dt_deal + pd.Timedelta(hours=8) dt_deal = dt_deal.strftime('%Y-%m-%d %H:%M:%S') df1['timestamp'] = pd.to_datetime(df1['timestamp'], unit='ms') # UTC 0时区的时间,格林威治的时间。 df1['timestamp'] = df1['timestamp'] + pd.Timedelta(hours=8) df1['timestamp'] = df1['timestamp'].strftime('%Y-%m-%d %H:%M:%S') total_all = binance.fetch_balance() total = float(total_all['BTC']['total']) * last + float(total_all['USDT']['total']) if binance.fetch_order(dingdanhao, 'BTC/USDT')['fee']: fee = binance.fetch_order(dingdanhao, 'BTC/USDT')['fee']['cost'] else: fee = 0 # 读取之前的数据,进行收益率的计算 df_profit = pd.read_csv('%srecord_volatility_btc.csv' % path_volatility, encoding="gbk") df_profit1 = float(df_profit.iloc[-1]['收益(美元)']) df_profit2 = df_profit.iloc[-1]['收益率'] # 计算当前预计年化收益 df_start = df_profit['记录时间'].iloc[0] df_start = pd.to_datetime(df_start) df_yield = float(df_profit2[:-1]) / ((df_end - df_start).days) * 365.25 df1['status'] = '完全成交' df1['side'] = '买入' fee = float(fee) * last a1 = '买入建仓' b1 = '满足开仓条件,已经买入开仓。' a = '%s' % a1 # a信息描述 b = '%s' % dt # b时间 c = '%.6f' % float(df1['amount']) # c委托数量 d = '%s' % df1['timestamp'] # d委托时间 e = '%.6f' % float(df1['filled']) # e成交数量 m = '%.2f' % float(df1['cost']) # m成交金额 f = '%.4f' % fee # f手续费 g = '%s' % df1['id'] # g订单ID h = '%s' % df1['price'] # h委托价格 n = '%.2f' % df1['average'] # i成交均价 j = '%s' % df1['status'] # g订单状态 k = '%s' % df1['side'] # k订单类型 l = '%.2f' % total # l账户总值(美元) o = '%.2f' % df_profit1 # o 收益(美元) p = '%s' % df_profit2 # p 收益率 q = '%.2f%%' % 0 # 本次交易收益率 r = '%.2f%%' % df_yield # s 年化收益率 dt7 = ['%s' % a, '%s' % b, '%s' % c, '%s' % d, '%s' % e, '%s' % m, '%s' % f, '%s \t' % g, '%s' % h, '%s' % n, '%s' % j, '%s' % k, '%s' % l, '%s' % o, '%s' % p, '%s' % q, '%s' % r] print('┏', '-' * 72, '┓') print(' ', '成交类型: %s' % dt7[11], "成交时间:%s" % dt7[1], '成交均价:%s' % dt7[9], '状态:%s' % dt7[10]) print('┗', '-' * 72, '┛') time.sleep(1) print("已经买入开仓,开始挂止盈止损单") # 止损触发价格,按照开仓的均价进行计算,一般是在均价的基础上下跌7.61%时触发 # 止损卖出价格,按照止损触发价格的0.98止损,防止因下跌过快而无法平仓 stop_price = float(df1['average']) * 0.9239 binance.create_order(symbol='BTC/USDT', type='STOP_LOSS_LIMIT', side='sell', amount=float(total_all['BTC']['total']), price=stop_price * 0.98, params={'stopPrice': stop_price}) record_volatility_btc(dt7) message = "%s\n信息描述:%s\n委托时间:%s \n成交时间:%s \n委托数量:%s \n成交数量:%s\n成交金额(美元):%s\n手续费(美元):%s\n订单" \ "ID:%s\n成交均价:%s\n订单状态:%s\n订单类型:%s\n账户总值(美元):%s " % ( b1, a, d, dt_deal, c, e, m, f, g, n, j, k, l) bot(message) print("消息发送完成") print('发送时间为:%s' % dt) print('+' * 50) # 为了节省流量,只要检查出成交单子后,全部处理完,就返回,不再进行下一步的检查 return def sell_out(x): # 读取之前的数据,进行收益率的计算 df_profit = pd.read_csv('%srecord_volatility_btc.csv' % path_volatility, encoding="gbk") df_profit1 = float(df_profit.iloc[-1]['收益(美元)']) last_buy_price = float(df_profit.iloc[-1]['成交均价']) last_trading_amount = float(df_profit.iloc[-1]['成交金额(美元)']) # 获取最新价格 last = float(binance.fetch_ticker('BTC/USDT')['last']) # 为了避免卖出失败,直接采用现价少2% 的价格一次性卖出,后续资金量大时,需要优化卖出流程 result_sell = binance.create_order(symbol='BTC/USDT', type='limit', side='sell', amount=x, price=last * 0.98) dingdanhao = result_sell['id'] # 等待5秒后查询订单 time.sleep(5) df1 = {} try: df1 = binance.fetch_order(dingdanhao, 'BTC/USDT') except: i_order = 0 while i_order < 30: # 正常为10,更改成10分钟为20 # 如果在前5秒钟内查询不到订单,就每隔30秒查询一次,连续查询5分钟 time.sleep(30) try: df1 = binance.fetch_order(dingdanhao, 'BTC/USDT') if df1: i_order = 41 except: pass i_order += 1 if not df1: df1 = binance.fetch_order(dingdanhao, 'BTC/USDT') if df1['status'] == 'closed' and df1['side'] == 'sell': # 订单状态 'open', 'closed', 'canceled': # 记录成交数据 # 获取当前时间 df_end = datetime.now() dt = df_end.strftime('%Y-%m-%d %H:%M:%S') # 增加订单成交时间 dt_deal = pd.to_datetime(df1['info']['updateTime'], unit='ms') # UTC 0时区的时间,格林威治的时间。 dt_deal = dt_deal + pd.Timedelta(hours=8) dt_deal = dt_deal.strftime('%Y-%m-%d %H:%M:%S') df1['timestamp'] = pd.to_datetime(df1['timestamp'], unit='ms') # UTC 0时区的时间,格林威治的时间。 df1['timestamp'] = df1['timestamp'] + pd.Timedelta(hours=8) df1['timestamp'] = df1['timestamp'].strftime('%Y-%m-%d %H:%M:%S') total_all = binance.fetch_balance() total = float(total_all['BTC']['total']) * last + float(total_all['USDT']['total']) if binance.fetch_order(dingdanhao, 'BTC/USDT')['fee']: fee = binance.fetch_order(dingdanhao, 'BTC/USDT')['fee']['cost'] else: fee = 0 df1['status'] = '完全成交' df1['side'] = '卖出' fee = float(fee) a1 = '卖出平仓' b1 = '满足平仓条件,已经卖出平仓。' # 计算收益 本次卖出价减去上一个买入价,除以上一次买入价,乘以开仓数量 this_time_profit = (float(df1['average']) - float(last_buy_price)) / float(last_buy_price) df_profit1 = df_profit1 + last_trading_amount * this_time_profit - (last_trading_amount * 0.002) # 每次需要扣除0.2%的双边手续费 df_profit2 = df_profit1 / 2000 # 当前总利润除以本金 # 计算当前预计年化收益 df_start = df_profit['记录时间'].iloc[0] df_start = pd.to_datetime(df_start) df_yield = df_profit2 * 100 / ((df_end - df_start).days) * 365.25 a = '%s' % a1 # a信息描述 b = '%s' % dt # b时间 c = '%.6f' % float(df1['amount']) # c委托数量 d = '%s' % df1['timestamp'] # d委托时间 e = '%.6f' % float(df1['filled']) # e成交数量 m = '%.2f' % float(df1['cost']) # m成交金额 f = '%.4f' % fee # f手续费 g = '%s' % df1['id'] # g订单ID h = '%s' % df1['price'] # h委托价格 n = '%.2f' % df1['average'] # i成交均价 j = '%s' % df1['status'] # g订单状态 k = '%s' % df1['side'] # k订单类型 l = '%.2f' % total # l账户总值(美元) o = '%.2f' % df_profit1 # o 收益(美元) p = '%.2f%%' % (df_profit2 * 100) # p 收益率 q = '%.2f%%' % (this_time_profit * 100) # 本次交易收益率 r = '%.2f%%' % df_yield # s 年化收益率 dt7 = ['%s' % a, '%s' % b, '%s' % c, '%s' % d, '%s' % e, '%s' % m, '%s' % f, '%s \t' % g, '%s' % h, '%s' % n, '%s' % j, '%s' % k, '%s' % l, '%s' % o, '%s' % p, '%s' % q, '%s' % r] print('┏', '-' * 72, '┓') print(' ', '成交类型: %s' % dt7[11], "成交时间:%s" % dt7[1], '成交均价:%s' % dt7[9], '状态:%s' % dt7[10]) print('┗', '-' * 72, '┛') time.sleep(1) record_volatility_btc(dt7) message = "%s\n信息描述:%s\n委托时间:%s \n成交时间:%s \n委托数量:%s \n成交数量:%s\n成交金额(美元):%s\n手续费(美元):%s\n订单" \ "ID:%s\n成交均价:%s\n订单状态:%s\n订单类型:%s\n本单交易收益率:%s\n账户总值(美元):%s " % ( b1, a, d, dt_deal, c, e, m, f, g, n, j, k, q, l) bot(message) print("消息发送完成") print('发送时间为:%s' % dt) print('+' * 50) # 为了节省流量,只要检查出成交单子后,全部处理完,就返回,不再进行下一步的检查 return def routine_inspection(): # 获取当前时间 d = datetime.now().date() - timedelta(days=5) d = time.strptime(str(d), '%Y-%m-%d') d = int(time.mktime(d)) * 1000 # 查看止盈止损单是否被触发 df_stop_loss_limit = binance.fetch_orders('BTC/USDT', since=d) df_stop_loss_limit = pd.DataFrame(df_stop_loss_limit) # 筛选出止盈止损单 df_stop_loss_limit = df_stop_loss_limit[df_stop_loss_limit['type'].isin(['stop_loss_limit'])] # 如果最新的这一个止盈止损单为closed,已成交状态,就进行记录 订单状态 'open', 'closed', 'canceled' if df_stop_loss_limit.iloc[-1]['status'] == 'closed' and df_stop_loss_limit.iloc[-1]['side'] == 'sell': # 遍历查询列表中的每一个订单号的状态 df1 = binance.fetch_order(int(df_stop_loss_limit.iloc[-1]['id']), 'BTC/USDT') # 读取之前的数据,进行收益率的计算 df_profit = pd.read_csv('%srecord_volatility_btc.csv' % path_volatility, encoding="gbk") df_profit1 = float(df_profit.iloc[-1]['收益(美元)']) last_buy_price = float(df_profit.iloc[-1]['成交均价']) last_trading_amount = float(df_profit.iloc[-1]['成交金额(美元)']) # 获取最新价格 last = float(binance.fetch_ticker('BTC/USDT')['last']) # 记录成交数据 # 获取当前时间 df_end = datetime.now() dt = df_end.strftime('%Y-%m-%d %H:%M:%S') # 增加订单成交时间 dt_deal = pd.to_datetime(df1['info']['updateTime'], unit='ms') # UTC 0时区的时间,格林威治的时间。 dt_deal = dt_deal + pd.Timedelta(hours=8) dt_deal = dt_deal.strftime('%Y-%m-%d %H:%M:%S') df1['timestamp'] = pd.to_datetime(df1['timestamp'], unit='ms') # UTC 0时区的时间,格林威治的时间。 df1['timestamp'] = df1['timestamp'] + pd.Timedelta(hours=8) df1['timestamp'] = df1['timestamp'].strftime('%Y-%m-%d %H:%M:%S') total_all = binance.fetch_balance() total = float(total_all['BTC']['total']) * last + float(total_all['USDT']['total']) if binance.fetch_order(df_stop_loss_limit.iloc[-1]['id'], 'BTC/USDT')['fee']: fee = binance.fetch_order(df_stop_loss_limit.iloc[-1]['id'], 'BTC/USDT')['fee']['cost'] else: fee = 0 df1['status'] = '完全成交' df1['side'] = '卖出' fee = float(fee) a1 = '止盈止损单触发卖出平仓' b1 = '止盈止损单已触发,卖出平仓。' # 计算收益 本次卖出价减去上一个买入价,除以上一次买入价,乘以开仓数量 this_time_profit = (float(df1['average']) - float(last_buy_price)) / float(last_buy_price) df_profit1 = df_profit1 + last_trading_amount * this_time_profit - (last_trading_amount * 0.002) # 每次需要扣除0.2%的双边手续费 df_profit2 = df_profit1 / 2000 # 当前总利润除以本金 # 计算当前预计年化收益 df_start = df_profit['记录时间'].iloc[0] df_start = pd.to_datetime(df_start) df_yield = df_profit2 * 100 / ((df_end - df_start).days) * 365.25 a = '%s' % a1 # a信息描述 b = '%s' % dt # b时间 c = '%.6f' % float(df1['amount']) # c委托数量 d = '%s' % df1['timestamp'] # d委托时间 e = '%.6f' % float(df1['filled']) # e成交数量 m = '%.2f' % float(df1['cost']) # m成交金额 f = '%.4f' % fee # f手续费 g = '%s' % df1['id'] # g订单ID h = '%s' % df1['price'] # h委托价格 n = '%.2f' % df1['average'] # i成交均价 j = '%s' % df1['status'] # g订单状态 k = '%s' % df1['side'] # k订单类型 l = '%.2f' % total # l账户总值(美元) o = '%.2f' % df_profit1 # o 收益(美元) p = '%.2f%%' % (df_profit2 * 100) # p 收益率 q = '%.2f%%' % (this_time_profit * 100) # 本次交易收益率 r = '%.2f%%' % df_yield # s 年化收益率 dt7 = ['%s' % a, '%s' % b, '%s' % c, '%s' % d, '%s' % e, '%s' % m, '%s' % f, '%s \t' % g, '%s' % h, '%s' % n, '%s' % j, '%s' % k, '%s' % l, '%s' % o, '%s' % p, '%s' % q, '%s' % r] print('┏', '-' * 72, '┓') print(' ', '成交类型: %s' % dt7[11], "成交时间:%s" % dt7[1], '成交均价:%s' % dt7[9], '状态:%s' % dt7[10]) print('┗', '-' * 72, '┛') time.sleep(1) record_volatility_btc(dt7) message = "%s\n信息描述:%s\n委托时间:%s \n成交时间:%s \n委托数量:%s \n成交数量:%s\n成交金额(美元):%s\n手续费(美元):%s\n订单" \ "ID:%s\n成交均价:%s\n订单状态:%s\n订单类型:%s\n本单交易收益率:%s\n账户总值(美元):%s " % ( b1, a, d, dt_deal, c, e, m, f, g, n, j, k, q, l) bot(message) print("消息发送完成") print('发送时间为:%s' % dt) print('+' * 50) # 为了节省流量,只要检查出成交单子后,全部处理完,就返回,不再进行下一步的检查 return def record_volatility_btc(dt): # 1.打开文件 with open("%srecord_volatility_btc.csv" % path_volatility, "a", encoding='gbk') as f: # 2.写入文件 f.write('\n') f.write(','.join(dt)) # 3.关闭文件 f.close() def bot(message): try: # WebHook地址 webhook = '' # 初始化机器人 xiaoming = DingtalkChatbot(webhook) # Text消息@所有人 True xiaoming.send_text(msg='行情提醒:%s' % message, is_at_all=False) except: i_order = 0 while i_order < 12: # 正常为12 # 如果第一次发送失败,就每隔5秒发送一次,连续发送1分钟 time.sleep(5) try: # WebHook地址 webhook = '' # 初始化机器人 xiaoming = DingtalkChatbot(webhook) # Text消息@所有人 True xiaoming.send_text(msg='行情提醒:%s' % message, is_at_all=False) i_order = 15 # 跳出循环 except: i_order += 1 def log(dt): # 1.打开文件 with open("%slog_volatility.txt" % path_volatility, "r+", encoding='gbk') as f: # 2.跳转指针到文首,并把内容写在开头 content = f.read() f.seek(0, 0) # 3.写入文件 f.write('\n') f.write('\n'.join(dt) + content) # 4.关闭文件 f.close() def dingding_message_volatility(dt): # 记录各类报错信息以及余额提醒 # 1.打开文件 with open("%sdingding_message_volatility.txt" % path_volatility, "r+", encoding='gbk') as f: # 2.跳转指针到文首,并把内容写在开头 content = f.read() f.seek(0, 0) # 3.写入文件 f.write('\n') f.write('\n'.join(dt) + content) # 4.关闭文件 f.close() def cancel_algo_order(): # 获取当前时间 d = datetime.now().date() - timedelta(days=5) d = time.strptime(str(d), '%Y-%m-%d') d = int(time.mktime(d)) * 1000 # 查看止盈止损单是否被触发 df_stop_loss_limit = binance.fetch_orders('BTC/USDT', since=d) df_stop_loss_limit = pd.DataFrame(df_stop_loss_limit) # 筛选出止盈止损单 df_stop_loss_limit = df_stop_loss_limit[df_stop_loss_limit['type'].isin(['stop_loss_limit'])] time.sleep(1) # 如果最新的这一个止盈止损单为open,未成交状态,就取消它 订单状态 'open', 'closed', 'canceled' if df_stop_loss_limit.iloc[-1]['status'] == 'open' and df_stop_loss_limit.iloc[-1]['side'] == 'sell': try: # 撤销线上的挂单 binance.cancel_order(id=int(df_stop_loss_limit.iloc[-1]['id']), symbol='BTC/USDT') except Exception as e: # 如果撤单失败,需要进行提醒 print(e) b = '错误原因为:%s' % e fengexian = '-' * 50 # 记录时的分隔线 # 获取当前时间 df_end = datetime.now() dt = df_end.strftime('%Y-%m-%d %H:%M:%S') dt1 = ['%s' % b + '\n' + '报错时间:%s' % dt + '\n' + '错误位置:2' + '\n' + '%s' % fengexian] log(dt1) print("程序报错准备返回主程序重启") message = "止盈止损单撤单失败,快去看看吧。" bot_message(dt, message) def bot_message(dt, message): # 消息提醒功能,每日不超过3次提醒 dt3 = ['%s' % dt] # 格式化当前的时间,方便下一步使用 dt4 = int(dt[0:4] + dt[5:7] + dt[8:10]) # 读取之前发送消息的时间 lines = pd.read_table('%sdingding_message_volatility.txt' % path_volatility, header=None, encoding='gb2312', sep=',') lastdate = lines.iloc[2] df_dingding_message = str(lastdate.iloc[0]) # 把读取到的数据进行格式统一,方便后续使用 df3 = int( df_dingding_message[0:4] + df_dingding_message[5:7] + df_dingding_message[ 8:10]) # 比对当前时间和读取到的时间,如果相同,说明一天内已经发送了3条信息了,关闭程序,等待问题解决。 if dt4 == df3: try: # 关闭程序 os._exit(0) except: # 程序有时无法正常关闭,采用暂停的方法变相关闭程序 time.sleep(3600) else: bot(message) dingding_message_volatility(dt3) print("消息发送完成") # 获取当前时间 df_end = datetime.now() dt = df_end.strftime('%Y-%m-%d %H:%M:%S') print('发送时间为:%s' % dt) print('+' * 50) time.sleep(5) if __name__ == '__main__': ''' 主程序, 用来调度各个重要流程 ''' print('开始执行') path_volatility = "/home/ubuntu/geren/volatility/" try: # 获取K线数据和转换数据 df = fetch_klines() # 根据转换后数据的倒数第二个信号判断开平仓,倒数第三个数据要为空值 if df['signal_long'].iloc[-2] == 1 and np.isnan(df['signal_long'].iloc[-3]): # 开仓信号 # 1.查看当前账户里是否有持仓,当前账户总资金是否超过0.001,说明有持仓,不进行开仓操作。 total_all = binance.fetch_balance()['BTC']['total'] if total_all > 0.001: # 账户里有持有,不开仓 发送信息并且关闭程序 df_end = datetime.now() dt = df_end.strftime('%Y-%m-%d %H:%M:%S') message = '满足开仓条件,当前账户里有持仓,未开仓,快去看看吧。' bot_message(dt, message) os._exit(1) else: # 开仓 采用固定金额开仓 buy_in() os._exit(1) elif df['signal_long'].iloc[-2] == 0 and np.isnan(df['signal_long'].iloc[-3]): # 平仓信号 total_all = binance.fetch_balance()['BTC'] if total_all['used'] > 0.001: # 如果有止盈止损单,先优先撤掉止盈止损单 cancel_algo_order() if total_all['total'] > 0.001: # 平仓 sell_out(total_all['total']) os._exit(1) else: routine_inspection() os._exit(1) except Exception as e: print(8) print(e) b = '错误原因为:%s' % e fengexian = '-' * 50 # 记录时的分隔线 dt = ['%s' % b + '\n' + '错误位置:2' + '\n' + '%s' % fengexian] log(dt) print("程序报错准备返回主程序重启") print('-' * 50) print("程序开始重启") print("重启时间为:") # 获取当前时间 df_end = datetime.now() dt = df_end.strftime('%Y-%m-%d %H:%M:%S') print(dt) a = "重启时间为:%s" % dt dt = ['%s' % a] log(dt) time.sleep(10) restart = sys.executable os.execl(restart, restart, *sys.argv)
如果大家对量化交易有啥建议可以加我QQ一起探讨交流
今天的文章python 交易自动化_python编写自动售货机分享到此就结束了,感谢您的阅读。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/84045.html