Browse Source

Merge branch 'master' of http://chenzheshi.myds.me:3000/Daniel/stock

Daniel 1 year ago
parent
commit
a38ed5c64d

+ 1 - 0
.idea/modules.xml

@@ -2,6 +2,7 @@
 <project version="4">
   <component name="ProjectModuleManager">
     <modules>
+      <module fileurl="file://$PROJECT_DIR$/../AI/.idea/AI.iml" filepath="$PROJECT_DIR$/../AI/.idea/AI.iml" />
       <module fileurl="file://$USER_HOME$/PycharmProjects/pythonProject/.idea/pythonProject.iml" filepath="$USER_HOME$/PycharmProjects/pythonProject/.idea/pythonProject.iml" />
       <module fileurl="file://$USER_HOME$/Library/CloudStorage/OneDrive-个人/个人/python_stocks/quantify01/.idea/quantify01.iml" filepath="$USER_HOME$/Library/CloudStorage/OneDrive-个人/个人/python_stocks/quantify01/.idea/quantify01.iml" />
       <module fileurl="file://$PROJECT_DIR$/.idea/stock.iml" filepath="$PROJECT_DIR$/.idea/stock.iml" />

+ 1 - 0
.idea/stock.iml

@@ -7,5 +7,6 @@
     <orderEntry type="module" module-name="stocks_to_sql" />
     <orderEntry type="module" module-name="quantify01" />
     <orderEntry type="module" module-name="pythonProject" />
+    <orderEntry type="module" module-name="AI" />
   </component>
 </module>

+ 1 - 1
.idea/vcs.xml

@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <project version="4">
   <component name="VcsDirectoryMappings">
-    <mapping directory="$PROJECT_DIR$" vcs="Git" />
+    <mapping directory="" vcs="Git" />
   </component>
 </project>

BIN
QMT/20230516105422482.dmp


+ 414 - 0
QMT/230504_real_time.py

@@ -0,0 +1,414 @@
+# coding:utf-8
+from datetime import datetime as dt
+import os
+import pandas as pd
+from xtquant.xttrader import XtQuantTrader, XtQuantTraderCallback
+from xtquant.xttype import StockAccount
+from xtquant import xtdata, xtconstant
+import time
+from sqlalchemy import create_engine, text
+from jqdatasdk import *
+import pymysql
+import multiprocessing as mp
+import math
+import psutil
+import datetime
+from apscheduler.schedulers.blocking import BlockingScheduler
+import sys
+
+# 指定客户端所在路径
+path = r'c:\\qmt\\userdata_mini'
+# 创建资金账号为 800068 的证券账号对象
+acc = StockAccount('920000207040', 'SECURITY')
+# 生成session id 整数类型 同时运行的策略不能重复
+session_id = 123456
+xt_trader = None
+engine_stock = create_engine('mysql+pymysql://root:r6kEwqWU9!v3@localhost:3307/qmt_stocks_whole?charset=utf8',
+                             pool_size=5000, pool_recycle=50, max_overflow=-1)
+auth('18616891214', 'Ea?*7f68nD.dafcW34d!')
+
+
+class MyXtQuantTraderCallback(XtQuantTraderCallback):
+    def on_disconnected(self):
+        """
+        连接断开
+        :return:
+        """
+        print(datetime.datetime.now(), '连接断开回调')
+
+    def on_stock_order(self, order):
+        """
+        委托回报推送
+        :param order: XtOrder对象
+        :return:
+        """
+        print(datetime.datetime.now(), '委托回调', order.order_remark)
+
+    def on_stock_trade(self, trade):
+        """
+        成交变动推送
+        :param trade: XtTrade对象
+        :return:
+        """
+        print(datetime.datetime.now(), '成交回调', trade.order_remark)
+
+    def on_order_error(self, order_error):
+        """
+        委托失败推送
+        :param order_error:XtOrderError 对象
+        :return:
+        """
+        # print("on order_error callback")
+        # print(order_error.order_id, order_error.error_id, order_error.error_msg)
+        print(f"委托报错回调 {order_error.order_remark} {order_error.error_msg}")
+
+    def on_cancel_error(self, cancel_error):
+        """
+        撤单失败推送
+        :param cancel_error: XtCancelError 对象
+        :return:
+        """
+        print(datetime.datetime.now(), sys._getframe().f_code.co_name)
+
+    def on_order_stock_async_response(self, response):
+        """
+        异步下单回报推送
+        :param response: XtOrderResponse 对象
+        :return:
+        """
+        print(f"异步委托回调 {response.order_remark}")
+
+    def on_cancel_order_stock_async_response(self, response):
+        """
+        :param response: XtCancelOrderResponse 对象
+        :return:
+        """
+        print(datetime.datetime.now(), sys._getframe().f_code.co_name)
+
+    def on_account_status(self, status):
+        """
+        :param response: XtAccountStatus 对象
+        :return:
+        """
+        print(datetime.datetime.now(), sys._getframe().f_code.co_name)
+
+
+def run(seq, pid):
+    mor = datetime.datetime.strptime(
+        str(dt.now().date()) + '11:30', '%Y-%m-%d%H:%M')
+    afternoon = datetime.datetime.strptime(
+        str(dt.now().date()) + '15:00', '%Y-%m-%d%H:%M')
+    mor_1 = datetime.datetime.strptime(
+        str(dt.now().date()) + '12:59', '%Y-%m-%d%H:%M')
+    """阻塞线程接收行情回调"""
+    import time
+    client = xtdata.get_client()
+    while True:
+        time.sleep(3)
+        now_date = dt.now()
+        if not client.is_connected():
+            xtdata.unsubscribe_quote(seq)
+            raise Exception('行情服务连接断开')
+        # if mor < dt.now() < mor_1:
+        #     xtdata.unsubscribe_quote(seq)
+        #     print(f'现在时间:{dt.now()},已休市')
+        #     sys.exit()
+        #     break
+        #     return 0
+        elif dt.now() > afternoon:
+            xtdata.unsubscribe_quote(seq)
+            print(f'现在时间:{dt.now()},已收盘')
+            sys.exit()
+            break
+            # return 0
+    return
+
+
+def get_fundamentals(results):
+    return results
+    pass
+
+
+def ma(stock, num, data):
+    global engine_stock
+    try:
+        i = (num - 1) * -1
+        df = pd.read_sql_query(text(
+            'select close_front from `%s_1d`' % stock), engine_stock.connect())
+    except BaseException as e:
+        print(e)
+        return 9999999
+    else:
+        ma_num = (sum(df['close_front'][i:]) + data[stock]['lastPrice']) / num
+        return ma_num
+
+
+def ma_1(stock, num):
+    global engine_stock
+    i = num * -1
+    try:
+        df = pd.read_sql_query(text(
+            'select close_front from `%s_1d`' % stock), engine_stock.connect())
+    except BaseException as e:
+        print(e)
+        return 9999999
+    else:
+        ma_num_1 = df['close_front'][i:].mean()
+        return ma_num_1
+
+
+def his_vol(stock, num):
+    global engine_stock
+    num = num * -1
+    try:
+        df = pd.read_sql_query(text(
+            'select volume_front from `%s_1d`' % stock), engine_stock.connect())
+    except BaseException:
+        return 9999999
+    else:
+        return df['volume_front'].iloc[num]
+
+
+def ma_judge(data, list_judge, rate, results):
+    # print(f'这个ma_judge的PID为:{os.getpid()},本轮计算:{len(list_judge)}个股')
+    for stock in list_judge:
+        current_price, open_price = data[stock]['lastPrice'], data[stock]['open']
+        MA5, MA10, MA20, MA30, MA60, MA120 = ma(stock, 5, data), ma(stock, 10, data), ma(stock, 20, data), ma(stock, 30,
+                                                                                                              data), \
+            ma(stock, 60, data), ma(stock, 120, data)
+        MA5_1 = ma_1(stock, 5)
+        # print(i, current_price, open_price, MA5, MA10, MA20, MA5_1)
+        # 入交易池标准:阳线\大于MA5\MA5向上\MA20<MA10\离120线有距离
+        if (current_price > open_price) & (current_price > MA5) & (MA5 > MA5_1) & (current_price < MA5 * 1.05) \
+                & (current_price > MA120 or current_price < MA120 * rate):
+            if his_vol(stock, -1) > his_vol(stock, -2):
+                results.append(stock.replace('SH', 'XSHG').replace('SZ', 'XSHE'))
+
+
+def sell_trader(data):
+    # print('卖出函数:', dt.now())
+    positions = xt_trader.query_stock_positions(acc)
+    positions_dict = {positions[x].stock_code: positions[x].can_use_volume for x in range(0, len(positions))}
+    print(
+        f'目前持仓总数为:{len([positions[x].stock_code for x in range(0, len(positions)) if positions[x].volume != 0])}')
+
+    for stock, can_use_volume in positions_dict.items():
+        if stock in data and can_use_volume != 0:
+            current_price = data[stock]['lastPrice']
+            open_price = data[stock]['open']
+            MA5 = ma(stock, 5, data)
+            MA5_1 = ma_1(stock, 5)
+            df = pd.read_sql_query(text(
+                'select close_front, high_front from `%s_1d`' % stock), engine_stock.connect())
+            print(f"{data[stock]['time']}, {stock},持仓量为{can_use_volume}当前价:{current_price},开盘价:{open_price},"
+                  f"MA5:{MA5},昨日MA5:{MA5_1},开始判断:")
+            if current_price == xtdata.get_instrument_detail(stock).get('UpStopPrice') \
+                    or (df['close_front'].iloc[-1] == df['high_front'].iloc[-1]
+                        and df['close_front'].iloc[-1] / df['close_front'].iloc[-2] > 1.08):
+                print(f"{stock}涨停或昨日涨幅超过8%,持股观察!{data[stock]['time']}")
+                continue
+            elif current_price < MA5 or MA5 < MA5_1:
+                print('卖出信号!!!!!!', stock, current_price)
+                order_id = xt_trader.order_stock(acc, stock, xtconstant.STOCK_SELL, can_use_volume,
+                                                 xtconstant.LATEST_PRICE, 0, 'MA5策略', '低于MA5趋势向下')
+                print('价格:', current_price, open_price, MA5, MA5_1, '低于MA5趋势向下')
+                print(order_id, stock, can_use_volume)
+            elif current_price > MA5 * 1.07:
+                print('盈利乖离率超7%!!!!!!', stock, current_price)
+                order_id = xt_trader.order_stock(acc, stock, xtconstant.STOCK_SELL, can_use_volume,
+                                                 xtconstant.LATEST_PRICE, 0, 'MA5策略', '盈利乖离率超7%')
+                print('价格:', current_price, open_price, MA5, MA5_1, '盈利乖离率超7%')
+                print(order_id, stock, can_use_volume)
+        else:
+            # print(f'本轮没有持仓股票信息!')
+            pass
+        engine_stock.dispose()
+
+
+def buy_trader(data):
+    # print('买入函数:', dt.now(), f'接受到{len(data.keys())}个个股')
+    results = mp.Manager().list()
+    mp_list = []
+    engine_hlfx_pool = create_engine('mysql+pymysql://root:r6kEwqWU9!v3@localhost:3307/hlfx_pool?charset=utf8',
+                                     pool_size=4000, pool_recycle=50, max_overflow=-1)
+
+    try:
+        stock_pool = pd.read_sql_query(
+            text('select value from `%s` order by `index` desc limit 10' % '1d'), engine_hlfx_pool.connect())
+        stock_pool = stock_pool.iloc[0, 0].split(",")
+        stock_pool.sort()
+        print('stock_pool', len(stock_pool))
+
+    except BaseException as e:
+        print(e)
+
+    if len(stock_pool) != 0:
+        list_judge = list(set(data.keys()) & set(stock_pool))
+        print(f'本轮有{len(data.keys())}条个股信息,而list_judge有:{len(list_judge)}')
+    else:
+        print(f'stock_pool为{len(stock_pool)}个')
+    step = math.ceil(len(list_judge) / 4)
+    rate = 0.8
+    if len(list_judge) != 0:
+        print(f'list_judge:{list_judge}')
+        for i in range(0, len(list_judge), step):
+            p = mp.Process(target=ma_judge, args=(data, list_judge[i:i + step], rate, results))
+            mp_list.append(p)
+            p.start()
+        for j in mp_list:
+            j.join()
+    results = list(set(results))
+
+    # 选择板块
+    if len(results) != 0:
+        print(f'进入板块选择{results}')
+        # 基本面过滤
+        results = get_fundamentals(results)
+        num_industry = get_industry(results)
+        industry_list = []
+        for key in num_industry.values():
+            for key2 in key.values():
+                industry_list.append(key2['industry_name'])
+        industry_list = pd.value_counts(industry_list)
+        # 最热集中的n个板块
+        max_industry_list = list(industry_list[0:2].index)
+        results_industry = []
+        for key, value in num_industry.items():
+            for key2 in value.values():
+                if key2['industry_name'] in max_industry_list:
+                    results_industry.append(key)
+        results_industry = ','.join(set(results_industry))
+        print('1d', '\n', results_industry)
+
+        db_pool = pymysql.connect(host='localhost',
+                                  user='root',
+                                  port=3307,
+                                  password='r6kEwqWU9!v3',
+                                  database='hlfx_pool')
+        cursor_pool = db_pool.cursor()
+        sql = "INSERT INTO MA5_%s (date,value) VALUES('%s','%s')" % ('1d', dt.now().strftime('%Y-%m-%d %H:%M:%S'),
+                                                                     results_industry)
+        cursor_pool.execute(sql)
+        db_pool.commit()
+
+        # print(len(results_industry), results_industry)
+        print(dt.now(), '数据库数据已赋值!')
+        cursor_pool.close()
+        db_pool.close()
+
+        # 取值交易
+        keep_stocks = results_industry.split(",")
+        new_keep_stock = [stock.replace('XSHG', 'SH').replace('XSHE', 'SZ') for stock in keep_stocks]
+        print(f'{dt.now()},new_keep_stock is:{len(new_keep_stock)}')
+
+        if len(new_keep_stock) != 0:
+            # 进入购买程序
+            max_pos = 7
+            for stock in new_keep_stock:
+                positions = xt_trader.query_stock_positions(acc)
+
+                asset = xt_trader.query_stock_asset(acc)
+                cash = asset.cash
+                positions_dict = {positions[x].stock_code: positions[x].volume for x in range(0, len(positions)) if
+                                  positions[x].volume > 0}
+                print(f'判断{stock}:cash={cash},持仓数量为{len(positions_dict)}')
+                current_price = data[stock]['lastPrice']
+                current_high = data[stock]['high']
+                if stock not in positions_dict:
+                    if len(positions_dict) < max_pos and current_price > 9 \
+                            and current_price > (current_high * 0.98):
+                        if 5000 > cash:
+                            i = 1
+                        else:
+                            i = 2
+                        volume = int((cash / i / current_price) // 100 * 100)
+                        print('买入信号!!!!!!', stock, volume, current_price)
+                        order_id = xt_trader.order_stock(acc, stock, xtconstant.STOCK_BUY, volume,
+                                                         xtconstant.LATEST_PRICE,
+                                                         current_price, 'MA5策略', 'MA5趋势向上')
+                        print(order_id)
+                    else:
+                        print(f'Cash只有:{cash} 或者 现有持仓{len(positions_dict)} 超过了{max_pos}')
+                else:
+                    print(f'{stock}已持仓!')
+    engine_hlfx_pool.dispose()
+    print('一轮结束了,现在时间是:', dt.now())
+
+
+def trader(data):
+    print(f'本轮订阅{len(data)}')
+    # print(f'xt_trader = {xt_trader},{session_id}')
+    # print(len(xt_trader.query_stock_positions(acc)))
+    # 卖出判断
+    sell_trader(data)
+    # 买入条件
+    buy_trader(data)
+
+
+def bridge():
+    global session_id, xt_trader
+    pid = os.getpid()
+    connect_result = -1
+    subscribe_result = -1
+
+    while True:
+        if connect_result != 0 or subscribe_result != 0:
+            session_id = int(time.time())
+            xt_trader = XtQuantTrader(path, session_id)
+            # 创建交易回调类对象,并声明接收回调
+            callback = MyXtQuantTraderCallback()
+            xt_trader.register_callback(callback)
+            # 启动交易线程
+            xt_trader.start()
+            # 建立交易连接,返回0表示连接成功
+            connect_result = xt_trader.connect()
+            print('建立交易连接,返回0表示连接成功', connect_result)
+            # 对交易回调进行订阅,订阅后可以收到交易主推,返回0表示订阅成功
+            subscribe_result = xt_trader.subscribe(acc)
+            print('对交易回调进行订阅,订阅后可以收到交易主推,返回0表示订阅成功', subscribe_result)
+            # 建立交易连接,返回0表示连接成功
+            connect_result = xt_trader.connect()
+            print('建立交易连接,返回0表示连接成功', connect_result)
+            # 对交易回调进行订阅,订阅后可以收到交易主推,返回0表示订阅成功
+            subscribe_result = xt_trader.subscribe(acc)
+            print('对交易回调进行订阅,订阅后可以收到交易主推,返回0表示订阅成功', subscribe_result)
+            time.sleep(3)
+        else:
+            break
+    print(f'MyPid is {os.getpid()}, now is {dt.now()},开盘了,session_id = {session_id}, \n')
+    positions = xt_trader.query_stock_positions(acc)
+    positions_dict = {positions[x].stock_code: positions[x].can_use_volume for x in range(0, len(positions))}
+    print(f'今日可卖出个股总数:{len([value for value in positions_dict.values() if value != 0])}')
+    stocks = xtdata.get_stock_list_in_sector('沪深A股')
+    seq = xtdata.subscribe_whole_quote(stocks, callback=trader)
+    run(seq, pid)
+
+
+def job_func():
+    print(f"Job started at {dt.now()}")
+    # 创建子进程
+    p = mp.Process(target=bridge)
+    # 启动子进程
+    p.start()
+    # 等待子进程结束
+    p.join()
+    print(f"Job finished at {dt.now()}")
+
+
+if __name__ == '__main__':
+    mp.freeze_support()
+    # print('cpu_count =', mp.cpu_count())
+    pus = psutil.Process()
+    pus.cpu_affinity([12, 13, 14, 15])
+
+    # job_func()
+
+    scheduler = BlockingScheduler()
+    scheduler.add_job(func=job_func, trigger='cron', day_of_week='0-4', hour='09', minute='40',
+                      timezone="Asia/Shanghai", max_instances=5)
+    # scheduler.add_job(func=job_func, trigger='cron', day_of_week='0-4', hour='12', minute='35',
+    #                   timezone="Asia/Shanghai")
+    try:
+        scheduler.start()
+    except (KeyboardInterrupt, SystemExit):
+        pass

+ 415 - 0
QMT/5m_data_whole.py

@@ -0,0 +1,415 @@
+from xtquant import xtdata
+from datetime import datetime as dt
+import pandas as pd
+import math
+from sqlalchemy import create_engine, text
+import multiprocessing as mp
+import os
+from apscheduler.schedulers.blocking import BlockingScheduler
+import traceback
+import psutil
+import pymysql
+import talib as ta
+import numpy as np
+
+
+pd.set_option('display.max_columns', None) # 设置显示最大行
+
+
+path = 'C:\\qmt\\userdata_mini'
+
+field = ['time', 'open', 'close', 'high', 'low', 'volume', 'amount']
+cpu_count = mp.cpu_count()
+
+
+def err_call_back(err):
+    print(f'问题在这里~ error:{str(err)}')
+    traceback.print_exc()
+
+
+def err_call_back(err):
+    print(f'出错啦~ error:{str(err)}')
+    traceback.print_exc()
+
+
+def myself_kdj(df):
+    low_list = df['low_back'].rolling(9, min_periods=9).min()
+    low_list.fillna(value=df['low_back'].expanding().min(), inplace=True)
+    high_list = df['high_back'].rolling(9, min_periods=9).max()
+    high_list.fillna(value=df['high_back'].expanding().max(), inplace=True)
+    rsv = (df['close_back'] - low_list) / (high_list - low_list) * 100
+    df['k'] = pd.DataFrame(rsv).ewm(com=2).mean()
+    df['d'] = df['k'].ewm(com=2).mean()
+    df['j'] = 3 * df['k'] - 2 * df['d']
+    return df
+
+
+# macd指标
+def get_macd_data(data, short=0, long1=0, mid=0):
+    if short == 0:
+        short = 12
+    if long1 == 0:
+        long1 = 26
+    if mid == 0:
+        mid = 9
+    data['sema'] = pd.Series(data['close_back']).ewm(span=short).mean()
+    data['lema'] = pd.Series(data['close_back']).ewm(span=long1).mean()
+    data.fillna(0, inplace=True)
+    data['dif'] = data['sema'] - data['lema']
+    data['dea'] = pd.Series(data['dif']).ewm(span=mid).mean()
+    data['macd'] = 2 * (data['dif'] - data['dea'])
+    data.fillna(0, inplace=True)
+    # return data[['dif', 'dea', 'macd']]
+
+
+# rsi指标
+def get_ris(data):
+    data["rsi_6"] = ta.RSI(data['close_back'], timeperiod=6)
+    data["rsi_12"] = ta.RSI(data['close_back'], timeperiod=12)
+    data["rsi_24"] = ta.RSI(data['close_back'], timeperiod=24)
+
+
+def get_bias(data):
+    # 计算方法:
+    # bias指标
+    # N期BIAS=(当日收盘价-N期平均收盘价)/N期平均收盘价*100%
+    data['bias_6'] = (data['close_back'] - data['close_back'].rolling(6, min_periods=1).mean()) / \
+                     data['close_back'].rolling(6, min_periods=1).mean() * 100
+    data['bias_12'] = (data['close_back'] - data['close_back'].rolling(12, min_periods=1).mean()) / \
+                      data['close_back'].rolling(12, min_periods=1).mean() * 100
+    data['bias_24'] = (data['close_back'] - data['close_back'].rolling(24, min_periods=1).mean()) / \
+                      data['close_back'].rolling(24, min_periods=1).mean() * 100
+    data['bias_6'] = round(data['bias_6'], 2)
+    data['bias_12'] = round(data['bias_12'], 2)
+    data['bias_24'] = round(data['bias_24'], 2)
+
+
+def get_wilr(data):
+    # 威廉指标
+    # 建议用talib库的WILLR方法,亲测有用
+    data['willr'] = ta.WILLR(data['high_back'], data['low_back'], data['close_back'], timeperiod=14)
+
+
+def get_hlfx(data):
+    Trading_signals = 0
+    data_temp = data[['time', 'open_back', 'close_back', 'high_back', 'low_back', 'dif', 'dea', 'macd']]
+    data_temp.columns = ['time', 'open', 'close', 'high', 'low', 'dif', 'dea', 'macd']
+    df_day = pd.DataFrame(columns=['time', 'open', 'close', 'high', 'low', 'volume', 'money', 'HL'])
+    # 先处理去包含
+    for i in data_temp.index:
+        if i == 0 or i == 1:
+            df_day = pd.concat([df_day, data_temp.iloc[[i]]], ignore_index=True)
+        # 不包含
+        elif (df_day.iloc[-1, 3] > data_temp.loc[i, 'high']
+              and df_day.iloc[-1, 4] > data_temp.loc[i, 'low']) \
+                or (df_day.iloc[-1, 3] < data_temp.loc[i, 'high']
+                    and df_day.iloc[-1, 4] < data_temp.loc[i, 'low']):
+            df_day = pd.concat([df_day, data_temp.loc[[i]]], ignore_index=True)
+        # 包含
+        else:
+            # 左高,下降
+            if df_day.iloc[-2, 3] > df_day.iloc[-1, 3]:
+                df_day.iloc[-1, 3] = min(df_day.iloc[-1, 3], data_temp.loc[i, 'high'])
+                df_day.iloc[-1, 4] = min(df_day.iloc[-1, 4], data_temp.loc[i, 'low'])
+            else:
+                # 右高,上升
+                df_day.iloc[-1, 3] = max(df_day.iloc[-1, 3], data_temp.loc[i, 'high'])
+                df_day.iloc[-1, 4] = max(df_day.iloc[-1, 4], data_temp.loc[i, 'low'])
+    # print('111', df_day, data_temp)
+
+    if len(df_day.index) > 2:
+        # 寻找顶底分型
+        for x in range(2, len(df_day.index)):
+            m = x - 1
+            # 底
+            # 符合底分型形态,且第2、3根k线是阳线
+            if ((df_day.loc[x, 'high'] > df_day.loc[x - 1, 'high']) and
+                (df_day.loc[x - 2, 'high'] > df_day.loc[x - 1, 'high'])):
+                # and df_day.loc[x, 'close'] > df_day.loc[x, 'open'] and \
+                #     df_day.loc[x - 1, 'close'] > df_day.loc[x - 1, 'open']:
+
+                df_day.loc[x, 'HL'] = 'L*'
+
+                while m:
+                    if df_day.loc[m, 'HL'] in ['H', 'HH', 'H*']:
+                        if (x - m) > 3:
+                            # 成笔——>L
+                            df_day.loc[x, 'HL'] = 'L'
+                            # 产生信号,进入hlfx_pool
+                            if x == len(df_day.index) - 1:
+                                Trading_signals = 1
+                        else:
+                            # 不成笔 次级别中枢,保持L* 修订原H为H*
+                            df_day.loc[m, 'HL'] = 'H*'
+                        break
+
+                    elif df_day.loc[m, 'HL'] in ['L', 'LL', 'L*']:
+                        if df_day.loc[m - 1, 'low'] > df_day.loc[x - 1, 'low']:
+                            # 前一个为底更高,且中间不存在更低的底
+                            df_day.loc[x, 'HL'] = 'L'
+                            df_day.loc[m, 'HL'] = '-'
+
+                            # 产生信号,进入hlfx_pool
+                            if x == len(df_day.index) - 1:
+                                Trading_signals = 1
+
+                            # 获得MACD,判断MACD判断背驰
+                            x_macd_dif, x_macd_dea, x_macd_macd = data_temp.loc[x, 'dif'], data_temp.loc[x, 'dea'], \
+                            data_temp.loc[x, 'macd']
+                            m_macd_dif, m_macd_dea, m_macd_macd = data_temp.loc[m, 'dif'], data_temp.loc[m, 'dea'], \
+                            data_temp.loc[m, 'macd']
+
+                            # MACD底背驰
+                            if m_macd_dif < x_macd_dif:
+                                # 次级别背驰底->LL
+                                df_day.loc[x, 'HL'] = 'LL'
+                            break
+                        else:
+                            # 前底更低,本底无效
+                            df_day.loc[x, 'HL'] = '-'
+                            break
+                    m = m - 1
+                    if m == 0:
+                        df_day.loc[x, 'HL'] = 'L'
+
+            # 顶
+            elif ((df_day.loc[x, 'high'] < df_day.loc[x - 1, 'high']) and (
+                    df_day.loc[x - 2, 'high'] < df_day.loc[x - 1, 'high'])):
+
+                df_day.loc[x, 'HL'] = 'H*'
+                while m:
+                    if df_day.loc[m, 'HL'] in ['L', 'LL', 'L*']:
+                        if x - m > 3:
+                            # 成笔->H
+                            df_day.loc[x, 'HL'] = 'H'
+                            # 产生信号,进入hlfx_pool
+                            if x == len(df_day.index) - 1:
+                                Trading_signals = 2
+                        else:
+                            # 不成笔 次级别中枢,保持H* 修订原L为L*
+                            df_day.loc[m, 'HL'] = 'L*'
+                        break
+
+                    elif df_day.loc[m, 'HL'] in ['H', 'HH', 'H*']:
+                        if df_day.loc[x - 1, 'high'] > df_day.loc[m - 1, 'high']:
+                            # 前一个为顶,且中间存在不包含 or 更高的顶
+                            df_day.loc[x, 'HL'] = 'H'
+                            df_day.loc[m, 'HL'] = '-'
+                            # 产生信号,进入hlfx_pool
+                            if x == len(df_day.index) - 1:
+                                Trading_signals = 2
+
+                            # 获得MACD,判断MACD判断背驰
+                            x_macd_dif, x_macd_dea, x_macd_macd = data_temp.loc[x, 'dif'], data_temp.loc[x, 'dea'], \
+                            data_temp.loc[x, 'macd']
+                            m_macd_dif, m_macd_dea, m_macd_macd = data_temp.loc[m, 'dif'], data_temp.loc[m, 'dea'], \
+                            data_temp.loc[m, 'macd']
+
+                            # MACD顶背驰
+                            if x_macd_dif < m_macd_dif:
+                                # 次级别背驰底->HH
+                                df_day.loc[x, 'HL'] = 'HH'
+                            break
+                        else:
+                            # 前顶更高,本顶无效
+                            df_day.loc[x, 'HL'] = '-'
+                            break
+                    m = m - 1
+                    if m == 0:
+                        df_day.loc[x, 'HL'] = 'H'
+
+            else:
+                df_day.loc[x, 'HL'] = '-'
+    df_temp = df_day[['time', 'HL']]
+
+    return df_temp, Trading_signals
+
+
+def tech_anal(stocks, hlfx_pool, hlfx_pool_daily, err_list):
+    print(f'{dt.now()}开始循环计算! MyPid is {os.getpid()},池子长度为{len(stocks)}')
+    m = 0
+
+    for stock in stocks:
+        engine = create_engine('mysql+pymysql://root:r6kEwqWU9!v3@localhost:3307/5m_stocks_whole?charset=utf8',
+                               pool_size=1, pool_recycle=7200, max_overflow=1000, pool_timeout=60)
+        engine_tech = create_engine('mysql+pymysql://root:r6kEwqWU9!v3@localhost:3307/5m_stocks_tech?charset=utf8',
+                                    pool_size=1, pool_recycle=3600, max_overflow=1000, pool_timeout=60)
+        # print(stock)
+        try:
+            df = pd.read_sql_table('%s_5m' % stock, con=engine.connect())
+            df.dropna(axis=0, how='any')
+            engine.dispose()
+        except BaseException:
+            print(f'{stock}读取有问题')
+            traceback.print_exc()
+            pass
+        else:
+            if len(df) != 0:
+                try:
+                    get_macd_data(df)
+                    get_ris(df)
+                    get_bias(df)
+                    get_wilr(df)
+                    df_temp, T_signals = get_hlfx(df)
+                    df = pd.merge(df, df_temp, on='time', how='left')
+                    df['HL'].fillna(value='-', inplace=True)
+                    df = df.reset_index(drop=True)
+                    # print(stock, '\n', df[['open_front', 'HL']])
+                    df = df.replace([np.inf, -np.inf], np.nan)
+                    df.to_sql('%s_5m' % stock, con=engine_tech, index=False, if_exists='replace')
+                    engine_tech.dispose()
+                # with engine.connect() as con:
+                #     con.execute("ALTER TABLE `%s_5m` ADD PRIMARY KEY (`time`);" % stock)
+                except BaseException as e:
+                    print(f'{stock}存储有问题', e)
+                    traceback.print_exc()
+                    err_list.append(stock)
+                    pass
+                else:
+                    # print(f"{stock} 成功!")
+                    m += 1
+            else:
+                err_list.append(stock)
+                print(f'{stock}数据为空')
+
+            if stock in hlfx_pool and T_signals == 2:
+                hlfx_pool.remove(stock)
+            elif stock not in hlfx_pool and T_signals == 1:
+                hlfx_pool.append(stock)
+                hlfx_pool_daily.append(stock)
+
+    print(f'Pid:{os.getpid()}已经完工了,应处理{len(stocks)},共计算{m}支个股')
+
+
+def ind():
+    sttime = dt.now()
+
+    stocks = xtdata.get_stock_list_in_sector('沪深A股')
+    print(len(stocks))
+    stocks.sort()
+
+    err_list = mp.Manager().list()
+    fre = '5m'
+    engine_hlfx_pool = create_engine('mysql+pymysql://root:r6kEwqWU9!v3@localhost:3307/hlfx_pool?charset=utf8',
+                                     pool_size=1, pool_recycle=3600, max_overflow=1000, pool_timeout=60)
+    hlfx_pool = mp.Manager().list()
+    hlfx_pool_daily = mp.Manager().list()
+    hlfx_pool.extend(pd.read_sql_query(
+        text("select value from %s" % fre), engine_hlfx_pool.connect()).iloc[-1, 0].split(","))
+
+    # pool = mp.Pool(processes=int(mp.cpu_count()))
+    # step = math.ceil(len(stocks) / mp.cpu_count())
+    pool = mp.Pool(processes=12)
+    step = math.ceil(len(stocks) / 12)
+    # step = 10000
+    # tech_anal(stocks, hlfx_pool)
+    for i in range(0, len(stocks), step):
+        pool.apply_async(func=tech_anal, args=(stocks[i:i + step], hlfx_pool, hlfx_pool_daily, err_list,),
+                         error_callback=err_call_back)
+    pool.close()
+    pool.join()
+    engine_hlfx_pool.dispose()
+
+    print(f'当日信号:{len(hlfx_pool_daily)},持续检测为:{len(hlfx_pool)}')
+    print(len(err_list), err_list)
+
+    results_list = ','.join(set(hlfx_pool))
+    results_list_daily = ','.join(set(hlfx_pool_daily))
+
+    # 存档入库
+    db_pool = pymysql.connect(host='localhost',
+                              user='root',
+                              port=3307,
+                              password='r6kEwqWU9!v3',
+                              database='hlfx_pool')
+    cursor_pool = db_pool.cursor()
+    sql = "INSERT INTO %s (date,value) VALUES('%s','%s')" % (fre, dt.now().strftime('%Y-%m-%d %H:%M:%S'), results_list)
+    cursor_pool.execute(sql)
+    db_pool.commit()
+
+    # 存档入库daily_5m
+    db_pool2 = pymysql.connect(host='localhost',
+                               user='root',
+                               port=3307,
+                               password='r6kEwqWU9!v3',
+                               database='hlfx_pool')
+    cursor_pool2 = db_pool2.cursor()
+    sql2 = "INSERT INTO daily_%s (date,value) VALUES('%s','%s')" % (fre, dt.now().strftime('%Y-%m-%d %H:%M:%S'),
+                                                                    results_list_daily)
+    cursor_pool2.execute(sql2)
+    db_pool2.commit()
+
+    edtime = dt.now()
+    print(edtime - sttime)
+
+
+def to_sql(stock_list):
+    print(f'{dt.now()}开始循环入库! MyPid is {os.getpid()}')
+    m = 0
+    for stock in stock_list:
+        eng_w = create_engine('mysql+pymysql://root:r6kEwqWU9!v3@localhost:3307/5m_stocks_whole?charset=utf8',
+                              pool_recycle=3600, pool_pre_ping=True, pool_size=1)
+        # 后复权数据
+        data_back = xtdata.get_market_data(field, [stock], '5m', end_time='', count=-1, dividend_type='back')
+        df_back = pd.concat([data_back[i].loc[stock].T for i in ['time', 'open', 'high', 'low', 'close', 'volume',
+                                                                 'amount']], axis=1)
+        df_back.columns = ['time', 'open_back', 'high_back', 'low_back', 'close_back', 'volume_back', 'amount_back']
+        df_back['time'] = df_back['time'].apply(lambda x: dt.fromtimestamp(x / 1000.0))
+        df_back.reset_index(drop=True, inplace=True)
+
+        # 前复权数据
+        data_front = xtdata.get_market_data(field, [stock], '5m', end_time='', count=-1, dividend_type='front')
+        df_front = pd.concat([data_front[i].loc[stock].T for i in ['time', 'open', 'high', 'low', 'close', 'volume',
+                                                                   'amount']], axis=1)
+        df_front.columns = ['time', 'open_front', 'high_front', 'low_front', 'close_front', 'volume_front',
+                            'amount_front']
+        df_front['time'] = df_front['time'].apply(lambda x: dt.fromtimestamp(x / 1000.0))
+        df = pd.merge_asof(df_back, df_front, 'time')
+        # print(df)
+        try:
+            # eng_w.connect().execute(text("truncate table `%s_5m`" % stock))
+            df.to_sql('%s_5m' % stock, con=eng_w, index=False, if_exists='replace', chunksize=5000)
+        except BaseException as e:
+            print(stock, e)
+            pass
+        else:
+            m += 1
+
+        eng_w.dispose()
+    print(f'Pid:{os.getpid()}已经完工了.应入库{len(stock_list)},共入库{m}支个股')
+
+
+def download_data():
+    stock_list = xtdata.get_stock_list_in_sector('沪深A股')
+    stock_list.sort()
+    print(dt.now(), '开始下载!')
+    # xtdata.download_history_data2(stock_list=stock_list, period='5m', start_time='', end_time='')
+    print(dt.now(), '下载完成,准备入库!')
+    # step = math.ceil(len(stock_list) / mp.cpu_count())
+    # pool = mp.Pool(processes=mp.cpu_count())
+    # pool = mp.Pool(processes=12)
+    # step = math.ceil(len(stock_list) / 12)
+    # for i in range(0, len(stock_list), step):
+    #     pool.apply_async(func=to_sql, args=(stock_list[i:i+step],), error_callback=err_call_back)
+    # pool.close()
+    # pool.join()
+    ind()
+
+    print(f'今日数据下载完毕 {dt.now()}')
+
+
+if __name__ == '__main__':
+    field = ['time', 'open', 'close', 'high', 'low', 'volume', 'amount']
+    cpu_count = mp.cpu_count()
+    pus = psutil.Process()
+    pus.cpu_affinity([8, 9, 10, 11, 16, 17, 18, 19, 20, 21, 22, 23])
+
+    download_data()
+
+    # scheduler = BlockingScheduler()
+    # scheduler.add_job(func=download_data, trigger='cron', day_of_week='0-4', hour='23', minute='05',
+    #                   timezone="Asia/Shanghai", max_instances=10)
+    # try:
+    #     scheduler.start()
+    # except (KeyboardInterrupt, SystemExit):
+    #     pass

+ 61 - 0
QMT/LSTM_1.py

@@ -0,0 +1,61 @@
+import pandas as pd
+import numpy as np
+from sklearn.preprocessing import MinMaxScaler
+from keras.models import Sequential
+from keras.layers import Dense, LSTM
+from ta.volatility import BollingerBands
+from ta.trend import MACD
+from ta.trend import EMAIndicator
+from ta.volume import MFIIndicator
+from sqlalchemy import create_engine
+
+# 加载数据
+engine_tech = create_engine('mysql+pymysql://root:r6kEwqWU9!v3@localhost:3307/qmt_stocks_tech?charset=utf8')
+df = pd.read_sql_table('600000.SH_1d', con=engine_tech)
+
+
+df = df[['open_back', 'high_back', 'low_back', 'close_back', 'volume_back', 'close_back', 'macd', 'willr', 'volume_back', 'dif', 'dea', 'rsi_6', 'rsi_12', 'rsi_24']].values
+
+# df = df[~np.isnan(df).any(axis=1), :]
+
+# 归一化
+scaler = MinMaxScaler(feature_range=(0, 1))
+scaled_data = scaler.fit_transform(df)
+print(len(df), len(scaled_data))
+
+# 构造训练数据
+lookback = 90
+target_days = 30
+
+x_train = []
+y_train = []
+for i in range(lookback, len(df) - target_days):
+    x_train.append(scaled_data[i - lookback:i])
+    y_train.append(scaled_data[i:i + target_days, 0])
+
+x_train, y_train = np.array(x_train), np.array(y_train)
+print('11111111111111111111', x_train.shape, y_train.shape)
+
+
+# 构建模型
+model = Sequential()
+model.add(LSTM(units=50, return_sequences=True, input_shape=(x_train.shape[1], x_train.shape[2])))
+model.add(LSTM(units=50, return_sequences=True))
+model.add(LSTM(units=50))
+model.add(Dense(units=30))
+
+model.compile(optimizer='adam', loss='mean_squared_error')
+
+model.fit(x_train, y_train, epochs=10, batch_size=32)
+
+# 预测未来30天走势
+test_data = scaled_data[-lookback:]
+test_data = np.expand_dims(test_data, axis=0)
+print(test_data.shape)
+predictions = model.predict(test_data)
+
+# 将归一化的预测数据转换为原始数据
+predictions = scaler.inverse_transform(predictions)
+
+# 打印预测结果
+print(predictions)

+ 376 - 8
QMT/chongxie_run.py

@@ -1,10 +1,16 @@
+# coding:utf-8
 from xtquant.xttrader import XtQuantTrader, XtQuantTraderCallback
 from xtquant.xttype import StockAccount
 from xtquant import xtdata, xtconstant
 from datetime import datetime as dt
+import pandas as pd
+import time
+import datetime
+import sys
+import os
 
-
-
+pd.set_option('display.max_columns', None) # 设置显示最大行
+"""
 def run():
     '''阻塞线程接收行情回调'''
     import time
@@ -12,15 +18,377 @@ def run():
     while True:
         time.sleep(3)
         now_date = dt.now()
-        if not client.is_connected() or dt.now() > now_date.replace(hour=11, minute=30, second=0):
+        if not client.is_connected() :
             raise Exception('行情服务连接断开')
             break
     return
 
+
 def trader(data):
-    print(dt.now(), len(data.keys()), data.keys())
+    # print('callback',os.getpid())
+    print(f'{dt.now()},len={len(data.keys())}')
+    print(data)
+
+
+# stocks = xtdata.get_stock_list_in_sector("沪深债券")
+# print(len(stocks))
+stocks = ['110043.SH', '110044.SH', '128075.SZ', '128079.SZ', '113017.SH', '128119.SZ']
+# # stocks = ['110055.SZ', '110061.SZ']
+stock_code = '127045.SZ'
+# seq2 = xtdata.subscribe_quote(stock_code, period='tick', start_time='20230101', end_time='20230407', count=10000, callback=trader)
+# # print(seq2)
+# seq = xtdata.subscribe_whole_quote(['沪深债券'], callback=trader)
+sss = xtdata.subscribe_whole_quote(stocks, callback=trader)
+# aaa = xtdata.subscribe_quote('128130.SZ', period='tick', start_time='', end_time='', count=0, callback=trader)
+#
+xtdata.run()
+exit()
+"""
+
+
+class MyXtQuantTraderCallback(XtQuantTraderCallback):
+    def on_disconnected(self):
+        """
+        连接断开
+        :return:
+        """
+        print(datetime.datetime.now(), '连接断开回调')
+
+    def on_stock_order(self, order):
+        """
+        委托回报推送
+        :param order: XtOrder对象
+        :return:
+        """
+        print(datetime.datetime.now(), '委托回调', order.order_remark)
+
+    def on_stock_trade(self, trade):
+        """
+        成交变动推送
+        :param trade: XtTrade对象
+        :return:
+        """
+        print(datetime.datetime.now(), '成交回调', trade.order_remark)
+
+    def on_order_error(self, order_error):
+        """
+        委托失败推送
+        :param order_error:XtOrderError 对象
+        :return:
+        """
+        # print("on order_error callback")
+        # print(order_error.order_id, order_error.error_id, order_error.error_msg)
+        print(f"委托报错回调 {order_error.order_remark} {order_error.error_msg}")
+
+    def on_cancel_error(self, cancel_error):
+        """
+        撤单失败推送
+        :param cancel_error: XtCancelError 对象
+        :return:
+        """
+        print(datetime.datetime.now(), sys._getframe().f_code.co_name)
+
+    def on_order_stock_async_response(self, response):
+        """
+        异步下单回报推送
+        :param response: XtOrderResponse 对象
+        :return:
+        """
+        print(f"异步委托回调 {response.order_remark}")
+
+    def on_cancel_order_stock_async_response(self, response):
+        """
+        :param response: XtCancelOrderResponse 对象
+        :return:
+        """
+        print(datetime.datetime.now(), sys._getframe().f_code.co_name)
+
+    def on_account_status(self, status):
+        """
+        :param response: XtAccountStatus 对象
+        :return:
+        """
+        print(datetime.datetime.now(), sys._getframe().f_code.co_name)
+
+
+def get_tick(code, start_time, end_time, period='tick'):
+    from xtquant import xtdata
+
+    xtdata.download_history_data(code, period=period, start_time=start_time, end_time=end_time)
+    data = xtdata.get_local_data(field_list=[], stock_code=[code], period=period)
+    result_list = data[code]
+    df = pd.DataFrame(result_list)
+
+    df['time'] = df['time'].apply(lambda x: datetime.datetime.fromtimestamp(x / 1000.0))
+    return df.iloc[-20:].loc[:, ['time', 'lastPrice', 'open', 'high','low','amount', 'volume']]
+    # return df.iloc[-20:,:]
+
+
+def process_timestamp(df, filename):
+    df = df.set_index('time_str')
+    result = df.resample('3S').first().ffill()
+    # result = result[(result.index >= '2022-07-20 09:30') & (result.index <= '2022-07-20 15:00')]
+    result = result.reset_index()
+    # result.to_csv(filename + '.csv')
+    print(result)
+
+
+def get_macd_data(data, short=0, long1=0, mid=0):
+    if short == 0:
+        short = 12
+    if long1 == 0:
+        long1 = 26
+    if mid == 0:
+        mid = 9
+    data['sema'] = pd.Series(data['close']).ewm(span=short).mean()
+    data['lema'] = pd.Series(data['close']).ewm(span=long1).mean()
+    data.fillna(0, inplace=True)
+    data['dif'] = data['sema'] - data['lema']
+    data['dea'] = pd.Series(data['dif']).ewm(span=mid).mean()
+    data['macd'] = 2 * (data['dif'] - data['dea'])
+    data.fillna(0, inplace=True)
+    return data
+
+
+def get_hlfx(data):
+    Trading_signals = 0
+    data_temp = data[['time', 'open', 'close', 'high', 'low', 'dif', 'dea', 'macd']]
+    data_temp.columns = ['time', 'open', 'close', 'high', 'low', 'dif', 'dea', 'macd']
+    df_day = pd.DataFrame(columns=['time', 'open', 'close', 'high', 'low', 'volume', 'money', 'HL'])
+    # 先处理去包含
+    for i in data_temp.index:
+        if i == 0 or i == 1:
+            df_day = pd.concat([df_day, data_temp.iloc[[i]]], ignore_index=True)
+        # 不包含
+        elif (df_day.iloc[-1, 3] > data_temp.loc[i, 'high']
+              and df_day.iloc[-1, 4] > data_temp.loc[i, 'low']) \
+                or (df_day.iloc[-1, 3] < data_temp.loc[i, 'high']
+                    and df_day.iloc[-1, 4] < data_temp.loc[i, 'low']):
+            df_day = pd.concat([df_day, data_temp.loc[[i]]], ignore_index=True)
+        # 包含
+        else:
+            # 左高,下降
+            if df_day.iloc[-2, 3] > df_day.iloc[-1, 3]:
+                df_day.iloc[-1, 3] = min(df_day.iloc[-1, 3], data_temp.loc[i, 'high'])
+                df_day.iloc[-1, 4] = min(df_day.iloc[-1, 4], data_temp.loc[i, 'low'])
+            else:
+                # 右高,上升
+                df_day.iloc[-1, 3] = max(df_day.iloc[-1, 3], data_temp.loc[i, 'high'])
+                df_day.iloc[-1, 4] = max(df_day.iloc[-1, 4], data_temp.loc[i, 'low'])
+    # print('111', df_day, data_temp)
+
+    if len(df_day.index) > 2:
+        # 寻找顶底分型
+        for x in range(2, len(df_day.index)):
+            m = x - 1
+            # 底
+            # 符合底分型形态,且第2、3根k线是阳线
+            if ((df_day.loc[x, 'high'] > df_day.loc[x - 1, 'high']) and
+                (df_day.loc[x - 2, 'high'] > df_day.loc[x - 1, 'high'])) and \
+                    df_day.loc[x, 'close'] > df_day.loc[x, 'open'] and \
+                    df_day.loc[x - 1, 'close'] > df_day.loc[x - 1, 'open']:
+
+                df_day.loc[x, 'HL'] = 'L*'
+                while m:
+                    if df_day.loc[m, 'HL'] in ['H', 'HH', 'H*']:
+                        if (x - m) > 3:
+                            # 成笔——>L
+                            df_day.loc[x, 'HL'] = 'L'
+                            # 产生信号,进入hlfx_pool
+                            if x == len(df_day.index) - 1:
+                                Trading_signals = 1
+
+                    elif df_day.loc[m, 'HL'] == 'L':
+                        if df_day.loc[m - 1, 'low'] > df_day.loc[x - 1, 'low']:
+                            # 前一个为底更高,且中间不存在更低的底
+                            df_day.loc[x, 'HL'] = 'L'
+
+                            # 产生信号,进入hlfx_pool
+                            if x == len(df_day.index) - 1:
+                                Trading_signals = 1
+
+                            # 获得MACD,判断MACD判断背驰
+                            x_macd_dif, x_macd_dea, x_macd_macd = data_temp.loc[x, 'dif'], data_temp.loc[x, 'dea'], \
+                            data_temp.loc[x, 'macd']
+                            m_macd_dif, m_macd_dea, m_macd_macd = data_temp.loc[m, 'dif'], data_temp.loc[m, 'dea'], \
+                            data_temp.loc[m, 'macd']
+
+                            # MACD底背驰
+                            if m_macd_dif < x_macd_dif:
+                                # 背驰底->LL
+                                df_day.loc[x, 'HL'] = 'LL'
+                            break
+                        break
+                    m = m - 1
+                    if m == 0:
+                        df_day.loc[x, 'HL'] = 'L'
+
+            # 顶
+            elif ((df_day.loc[x, 'high'] < df_day.loc[x - 1, 'high']) and (
+                    df_day.loc[x - 2, 'high'] < df_day.loc[x - 1, 'high'])):
+
+                df_day.loc[x, 'HL'] = 'H*'
+                while m:
+                    if df_day.loc[m, 'HL'] in ['L', 'LL', 'L*']:
+                        if x - m > 3:
+                            # 成笔->H
+                            df_day.loc[x, 'HL'] = 'H'
+                            # 产生信号,进入hlfx_pool
+                            if x == len(df_day.index) - 1:
+                                Trading_signals = 2
+
+                    elif df_day.loc[m, 'HL'] == 'H':
+                        if df_day.loc[x - 1, 'high'] > df_day.loc[m - 1, 'high']:
+                            # 前一个为顶,且中间存在不包含 or 更高的顶
+                            df_day.loc[x, 'HL'] = 'H'
+                            # 产生信号,进入hlfx_pool
+                            if x == len(df_day.index) - 1:
+                                Trading_signals = 2
+
+                            # 获得MACD,判断MACD判断背驰
+                            x_macd_dif, x_macd_dea, x_macd_macd = data_temp.loc[x, 'dif'], data_temp.loc[x, 'dea'], \
+                            data_temp.loc[x, 'macd']
+                            m_macd_dif, m_macd_dea, m_macd_macd = data_temp.loc[m, 'dif'], data_temp.loc[m, 'dea'], \
+                            data_temp.loc[m, 'macd']
+
+                            # MACD顶背驰
+                            if x_macd_dif < m_macd_dif:
+                                df_day.loc[x, 'HL'] = 'HH'
+                            break
+                        break
+                    m = m - 1
+                    if m == 0:
+                        df_day.loc[x, 'HL'] = 'H'
+
+            else:
+                df_day.loc[x, 'HL'] = '-'
+    df_temp = df_day[['time', 'HL']]
+
+    return df_temp, Trading_signals
+
+
+def ma(df, num):
+    i = -1 * (5 + num)
+    try:
+        if i == -5:
+            ma_num = sum(df['close'][i:])/5
+        else:
+            ma_num = (sum(df['close'][i:(i+5)]))/5
+        # print(df['close'][i:(i+5)])
+    except:
+        return 9999999
+    else:
+        return ma_num
+
+
+def cont(data):
+    # print(dt.now())
+    code = '127045.SZ'
+    dd = xtdata.get_market_data(field_list=[], stock_list=[code], period='tick', start_time='20230101', end_time='',
+                                count=-1, dividend_type='none', fill_data=True)
+    # print(dt.now(), type(dd), dd[code].shape)
+    # print(dd[code]['time'][-10:-1], '\n', dd[code]['lastPrice'][-10:-1], '\n', dd[code]['open'][-10:-1], '\n', dd[code]['high'][-10:-1])
+    # print(dd[code], '\n'*2)
+    dic = {'time': dd[code]['time'],
+           'open': dd[code]['open'],
+           'close': dd[code]['lastPrice'],
+           'high': dd[code]['high'],
+           'low': dd[code]['low']}
+    df_day = pd.DataFrame(dic)
+    df_day['time'] = df_day['time'].apply(lambda x: dt.fromtimestamp(x / 1000.0))
+    ma_now = ma(df_day, 0)
+    ma_1 = ma(df_day, 1)
+    ma_2 = ma(df_day, 2)
+    ma_3 = ma(df_day,3)
+    # print(ma_now,ma_1,ma_2)
+
+    positions = xt_trader.query_stock_positions(acc)
+    positions_dict = {positions[x].stock_code: positions[x].can_use_volume for x in range(0, len(positions))}
+    # print(positions_dict)
+
+    if code in positions_dict and positions_dict[code] != 0:
+        if df_day['close'].iloc[-1] < ma_now or ma_now < ma_1:
+            order_id = xt_trader.order_stock(acc, code, xtconstant.STOCK_SELL, positions_dict[code],
+                                             xtconstant.LATEST_PRICE, 0, '可转债', '滞涨')
+            print(f'可转债卖出_{order_id},成交价格:{xtconstant.LATEST_PRICE}')
+    # if df_day['close'].iloc[-1] > ma_now > ma_1 and ma_1 < ma_2 <= ma_3 and df_day['close'].iloc[-2] < ma_1:
+    #     print('buy:',  df_day['time'].iloc[-1], df_day['close'].iloc[-1], ma_now)
+    #     order_id = xt_trader.order_stock(acc, code, xtconstant.STOCK_BUY, 10, xtconstant.LATEST_PRICE,
+    #                                      0, '可转债', '跳多MA5')
+    #     print(f'可转债Buy——{order_id},成交价格:{xtconstant.LATEST_PRICE}')
+    if df_day['close'].iloc[-1] > df_day['close'].iloc[-2] and df_day['close'].iloc[-4] >df_day['close'].iloc[-3] > df_day['close'].iloc[-2]:
+        print('buy:', df_day['time'].iloc[-1], df_day['close'].iloc[-1], ma_now)
+        order_id = xt_trader.order_stock(acc, code, xtconstant.STOCK_BUY, 10, xtconstant.LATEST_PRICE, 0, '可转债', 'tick底分型')
+        print(f'可转债Buy——{order_id},成交价格:{xtconstant.LATEST_PRICE}')
+    # df_day = get_macd_data(df_day)
+    # df_day, Trading_signals= get_hlfx(df_day)
+    # print(df_day)
+    if dic ==1:
+        pass
+    elif dic ==1:
+        pass
+
+
+def dump_single_code_tick():
+    # 导出单个转债的tick数据
+    code='127045'
+    start_date = '20230101'
+    end_date = '202304010'
+
+    post_fix = 'SZ' if code.startswith('12') else 'SH'
+    code = '{}.{}'.format(code,post_fix)
+    print(code)
+    filename = '{}'.format(code)
+    df = get_tick(code, start_date, end_date)
+    print(df)
+
+
+if __name__ == '__main__':
+    # 指定客户端所在路径
+    path = r'c:\\qmt\\userdata_mini'
+    # 生成session id 整数类型 同时运行的策略不能重复
+    session_id = int(time.time())
+    xt_trader = XtQuantTrader(path, session_id)
+    # 创建资金账号为 800068 的证券账号对象
+    acc = StockAccount('920000207040', 'SECURITY')
+    # 创建交易回调类对象,并声明接收回调
+    callback = MyXtQuantTraderCallback()
+    xt_trader.register_callback(callback)
+    # 启动交易线程
+    xt_trader.start()
+    # 建立交易连接,返回0表示连接成功
+    connect_result = xt_trader.connect()
+    print('建立交易连接,返回0表示连接成功', connect_result)
+    # 对交易回调进行订阅,订阅后可以收到交易主推,返回0表示订阅成功
+    subscribe_result = xt_trader.subscribe(acc)
+    print('对交易回调进行订阅,订阅后可以收到交易主推,返回0表示订阅成功', subscribe_result)
+
+    code = '127045.SZ'
+    st = dt.now()
+    stocks = xtdata.get_stock_list_in_sector('沪深A股')
+    xtdata.subscribe_quote(code, 'tick', start_time='', end_time='', count=1000, callback=cont)
+    # xtdata.subscribe_whole_quote(stocks,callback=cont)
+    # dd = xtdata.get_market_data(field_list=[], stock_list=[code], period='tick', start_time='20230101', end_time='',
+    #                             count=-1,
+    #                             dividend_type='none', fill_data=True)
+
+    # print(positions_dict[code])
+    # print(type(dd), type(dd[code]), type(dd[code][-1]), dd[code], dd)
+    # print(dd[code]['lastPrice'])
+    # dd[code]['time'] = dd[code]['time'].apply(lambda x: dt.fromtimestamp(x / 1000.0))
+    # print(type(dd[code]['time']), dd[code]['time'])
+    # print('________','\n')
+    # print(type(list(dd[code]['time'])))
+    # print('________')
+    # print(dd[code])
+    xtdata.run()
+    # dump_single_code_tick()
+
+    et = dt.now()
+    print(et-st)
+
+
+
+
+
 
-# stocks = stocks = xtdata.get_stock_list_in_sector('沪深A股')
-stocks = ['000001.SZ', '600000.SH', '300389.SZ', '001229.SZ', '600674.SH', '000895.SZ']
-xtdata.subscribe_whole_quote(stocks, callback=trader)
-run()

+ 23 - 3
QMT/demo.py

@@ -4,6 +4,8 @@ from xtquant import xtdata
 from xtquant.xttrader import XtQuantTrader, XtQuantTraderCallback
 from xtquant.xttype import StockAccount
 from xtquant import xtconstant
+from sqlalchemy import create_engine
+import pandas as pd
 
 #定义一个类 创建类的实例 作为状态的容器
 class _a():
@@ -99,6 +101,17 @@ class MyXtQuantTraderCallback(XtQuantTraderCallback):
 
 
 if __name__ == '__main__':
+    engine = create_engine('mysql+pymysql://root:r6kEwqWU9!v3@localhost:3307/qmt_stocks_tech?charset=utf8')
+    df = pd.read_sql_table('601155.SH_1d', engine)
+    df = df[df['HL'] != '-']
+    print(df)
+    df.to_csv(f"C:\Daniel\策略\601155.csv", index=True, encoding='utf_8_sig',
+              mode='w')
+
+
+
+
+    exit()
     print("start")
     #指定客户端所在路径
     path = r'c:\\qmt\\userdata_mini'
@@ -118,9 +131,16 @@ if __name__ == '__main__':
     # 对交易回调进行订阅,订阅后可以收到交易主推,返回0表示订阅成功
     subscribe_result = xt_trader.subscribe(acc)
     print('对交易回调进行订阅,订阅后可以收到交易主推,返回0表示订阅成功', subscribe_result)
-    # asset = xt_trader.query_stock_asset(acc)
-    # cash = asset.cash
-    # print(cash)
+    asset = xt_trader.query_stock_asset(acc)
+    cash = asset.cash
+    positions = xt_trader.query_stock_positions(acc)
+
+    print(cash, positions)
+    # xt_trader.run_forever()
+    print('run over')
+    if '600362.SH' == positions[0].stock_code:
+        print('aaa')
+    exit()
     # xtdata.subscribe_whole_quote(["SH", "SZ"], callback=f)
     xtdata.run()
 

+ 18 - 9
QMT/download_data_whole.py

@@ -2,11 +2,12 @@ from xtquant import xtdata
 from datetime import datetime as dt
 import pandas as pd
 import math
-from sqlalchemy import create_engine
+from sqlalchemy import create_engine, text
 import multiprocessing as mp
 import os
 from apscheduler.schedulers.blocking import BlockingScheduler
 import traceback
+import psutil
 
 
 pd.set_option('display.max_columns', None) # 设置显示最大行
@@ -16,7 +17,8 @@ path = 'C:\\qmt\\userdata_mini'
 
 field = ['time', 'open', 'close', 'high', 'low', 'volume', 'amount']
 cpu_count = mp.cpu_count()
-eng_w = create_engine('mysql+pymysql://root:r6kEwqWU9!v3@localhost:3307/qmt_stocks_whole?charset=utf8')
+
+
 
 
 def err_call_back(err):
@@ -28,6 +30,8 @@ def to_sql(stock_list):
     print(f'{dt.now()}开始循环入库! MyPid is {os.getpid()}')
     m = 0
     for stock in stock_list:
+        eng_w = create_engine('mysql+pymysql://root:r6kEwqWU9!v3@localhost:3307/qmt_stocks_whole?charset=utf8',
+                              pool_recycle=3600, pool_pre_ping=True, pool_size=1)
         # 后复权数据
         data_back = xtdata.get_market_data(field, [stock], '1d', end_time='', count=-1, dividend_type='back')
         df_back = pd.concat([data_back[i].loc[stock].T for i in ['time', 'open', 'high', 'low', 'close', 'volume',
@@ -46,12 +50,15 @@ def to_sql(stock_list):
         df = pd.merge_asof(df_back, df_front, 'time')
         # print(df)
         try:
-            df.to_sql('%s_1d' % stock, con=eng_w, index=True, if_exists='replace')
-        except BaseException:
-            print(stock)
+            # eng_w.connect().execute(text("truncate table `%s_1d`" % stock))
+            df.to_sql('%s_1d' % stock, con=eng_w, index=False, if_exists='replace', chunksize=20000)
+        except BaseException as e:
+            print(stock, e)
             pass
         else:
             m += 1
+
+        eng_w.dispose()
     print(f'Pid:{os.getpid()}已经完工了.应入库{len(stock_list)},共入库{m}支个股')
 
 
@@ -63,6 +70,8 @@ def download_data():
     print(dt.now(), '下载完成,准备入库!')
     step = math.ceil(len(stock_list) / mp.cpu_count())
     pool = mp.Pool(processes=mp.cpu_count())
+    # pool = mp.Pool(processes=8)
+    # step = math.ceil(len(stock_list) / 8)
     for i in range(0, len(stock_list), step):
         pool.apply_async(func=to_sql, args=(stock_list[i:i+step],), error_callback=err_call_back)
     pool.close()
@@ -71,17 +80,17 @@ def download_data():
     print(f'今日数据下载完毕 {dt.now()}')
 
 
-
 if __name__ == '__main__':
     field = ['time', 'open', 'close', 'high', 'low', 'volume', 'amount']
     cpu_count = mp.cpu_count()
-
+    pus = psutil.Process()
+    # pus.cpu_affinity([12, 13, 14, 15, 16, 17, 18, 19])
 
     # download_data()
 
     scheduler = BlockingScheduler()
-    scheduler.add_job(func=download_data, trigger='cron', day_of_week='0-4', hour='15', minute='40',
-                      timezone="Asia/Shanghai")
+    scheduler.add_job(func=download_data, trigger='cron', day_of_week='0-4', hour='20', minute='05',
+                      timezone="Asia/Shanghai", max_instances=10)
     try:
         scheduler.start()
     except (KeyboardInterrupt, SystemExit):

+ 63 - 23
QMT/qmt_get_indicators.py

@@ -4,7 +4,7 @@ import numpy as np
 import os
 import pandas as pd
 import time
-from sqlalchemy import create_engine
+from sqlalchemy import create_engine, text
 from jqdatasdk import *
 import pymysql
 import multiprocessing as mp
@@ -13,9 +13,10 @@ import talib as ta
 from xtquant import xtdata
 import os
 import traceback
+from apscheduler.schedulers.blocking import BlockingScheduler
+import psutil
 
 pd.set_option('display.max_columns', None)  # 设置显示最大行
-engine = create_engine('mysql+pymysql://root:r6kEwqWU9!v3@localhost:3307/qmt_stocks_whole?charset=utf8')
 
 
 def err_call_back(err):
@@ -54,7 +55,6 @@ def get_macd_data(data, short=0, long1=0, mid=0):
 
 
 # rsi指标
-# 建议用talib库的RSI方法,亲测有用
 def get_ris(data):
     data["rsi_6"] = ta.RSI(data['close_back'], timeperiod=6)
     data["rsi_12"] = ta.RSI(data['close_back'], timeperiod=12)
@@ -116,11 +116,12 @@ def get_hlfx(data):
             # 底
             # 符合底分型形态,且第2、3根k线是阳线
             if ((df_day.loc[x, 'high'] > df_day.loc[x - 1, 'high']) and
-                (df_day.loc[x - 2, 'high'] > df_day.loc[x - 1, 'high'])) and \
-                    df_day.loc[x, 'close'] > df_day.loc[x, 'open'] and \
-                    df_day.loc[x - 1, 'close'] > df_day.loc[x - 1, 'open']:
+                (df_day.loc[x - 2, 'high'] > df_day.loc[x - 1, 'high'])):
+                # and df_day.loc[x, 'close'] > df_day.loc[x, 'open'] and \
+                #     df_day.loc[x - 1, 'close'] > df_day.loc[x - 1, 'open']:
 
                 df_day.loc[x, 'HL'] = 'L*'
+
                 while m:
                     if df_day.loc[m, 'HL'] in ['H', 'HH', 'H*']:
                         if (x - m) > 3:
@@ -129,11 +130,16 @@ def get_hlfx(data):
                             # 产生信号,进入hlfx_pool
                             if x == len(df_day.index) - 1:
                                 Trading_signals = 1
+                        else:
+                            # 不成笔 次级别中枢,保持L* 修订原H为H*
+                            df_day.loc[m, 'HL'] = 'H*'
+                        break
 
-                    elif df_day.loc[m, 'HL'] == 'L':
+                    elif df_day.loc[m, 'HL'] in ['L', 'LL', 'L*']:
                         if df_day.loc[m - 1, 'low'] > df_day.loc[x - 1, 'low']:
                             # 前一个为底更高,且中间不存在更低的底
                             df_day.loc[x, 'HL'] = 'L'
+                            df_day.loc[m, 'HL'] = '-'
 
                             # 产生信号,进入hlfx_pool
                             if x == len(df_day.index) - 1:
@@ -147,10 +153,13 @@ def get_hlfx(data):
 
                             # MACD底背驰
                             if m_macd_dif < x_macd_dif:
-                                # 背驰底->LL
+                                # 次级别背驰底->LL
                                 df_day.loc[x, 'HL'] = 'LL'
                             break
-                        break
+                        else:
+                            # 前底更低,本底无效
+                            df_day.loc[x, 'HL'] = '-'
+                            break
                     m = m - 1
                     if m == 0:
                         df_day.loc[x, 'HL'] = 'L'
@@ -168,11 +177,16 @@ def get_hlfx(data):
                             # 产生信号,进入hlfx_pool
                             if x == len(df_day.index) - 1:
                                 Trading_signals = 2
+                        else:
+                            # 不成笔 次级别中枢,保持H* 修订原L为L*
+                            df_day.loc[m, 'HL'] = 'L*'
+                        break
 
-                    elif df_day.loc[m, 'HL'] == 'H':
+                    elif df_day.loc[m, 'HL'] in ['H', 'HH', 'H*']:
                         if df_day.loc[x - 1, 'high'] > df_day.loc[m - 1, 'high']:
                             # 前一个为顶,且中间存在不包含 or 更高的顶
                             df_day.loc[x, 'HL'] = 'H'
+                            df_day.loc[m, 'HL'] = '-'
                             # 产生信号,进入hlfx_pool
                             if x == len(df_day.index) - 1:
                                 Trading_signals = 2
@@ -185,9 +199,13 @@ def get_hlfx(data):
 
                             # MACD顶背驰
                             if x_macd_dif < m_macd_dif:
+                                # 次级别背驰底->HH
                                 df_day.loc[x, 'HL'] = 'HH'
                             break
-                        break
+                        else:
+                            # 前顶更高,本顶无效
+                            df_day.loc[x, 'HL'] = '-'
+                            break
                     m = m - 1
                     if m == 0:
                         df_day.loc[x, 'HL'] = 'H'
@@ -201,15 +219,18 @@ def get_hlfx(data):
 
 def tech_anal(stocks, hlfx_pool, hlfx_pool_daily, err_list):
     print(f'{dt.now()}开始循环计算! MyPid is {os.getpid()},池子长度为{len(stocks)}')
-
-    engine_tech = create_engine('mysql+pymysql://root:r6kEwqWU9!v3@localhost:3307/qmt_stocks_tech?charset=utf8')
     m = 0
 
     for stock in stocks:
+        engine = create_engine('mysql+pymysql://root:r6kEwqWU9!v3@localhost:3307/qmt_stocks_whole?charset=utf8',
+                               pool_size=1, pool_recycle=7200, max_overflow=1000, pool_timeout=60)
+        engine_tech = create_engine('mysql+pymysql://root:r6kEwqWU9!v3@localhost:3307/qmt_stocks_tech?charset=utf8',
+                                    pool_size=1, pool_recycle=3600, max_overflow=1000, pool_timeout=60)
         # print(stock)
         try:
-            df = pd.read_sql_table('%s_1d' % stock, con=engine)
+            df = pd.read_sql_table('%s_1d' % stock, con=engine.connect())
             df.dropna(axis=0, how='any')
+            engine.dispose()
         except BaseException:
             print(f'{stock}读取有问题')
             traceback.print_exc()
@@ -228,6 +249,7 @@ def tech_anal(stocks, hlfx_pool, hlfx_pool_daily, err_list):
                     # print(stock, '\n', df[['open_front', 'HL']])
                     df = df.replace([np.inf, -np.inf], np.nan)
                     df.to_sql('%s_1d' % stock, con=engine_tech, index=False, if_exists='replace')
+                    engine_tech.dispose()
                 # with engine.connect() as con:
                 #     con.execute("ALTER TABLE `%s_1d` ADD PRIMARY KEY (`time`);" % stock)
                 except BaseException:
@@ -248,10 +270,12 @@ def tech_anal(stocks, hlfx_pool, hlfx_pool_daily, err_list):
                 hlfx_pool.append(stock)
                 hlfx_pool_daily.append(stock)
 
+
+
     print(f'Pid:{os.getpid()}已经完工了,应处理{len(stocks)},共计算{m}支个股')
 
 
-if __name__ == '__main__':
+def ind():
     sttime = dt.now()
 
     stocks = xtdata.get_stock_list_in_sector('沪深A股')
@@ -260,25 +284,26 @@ if __name__ == '__main__':
 
     err_list = mp.Manager().list()
     fre = '1d'
-    engine_hlfx_pool = create_engine('mysql+pymysql://root:r6kEwqWU9!v3@localhost:3307/hlfx_pool?charset=utf8')
+    engine_hlfx_pool = create_engine('mysql+pymysql://root:r6kEwqWU9!v3@localhost:3307/hlfx_pool?charset=utf8',
+                                     pool_size=1, pool_recycle=3600, max_overflow=1000, pool_timeout=60)
     hlfx_pool = mp.Manager().list()
     hlfx_pool_daily = mp.Manager().list()
     hlfx_pool.extend(pd.read_sql_query(
-        'select value from `%s`' % fre, engine_hlfx_pool).iloc[-1, 0].split(","))
+        text("select value from %s" % fre), engine_hlfx_pool.connect()).iloc[-1, 0].split(","))
 
-    pool = mp.Pool(processes=mp.cpu_count())
+    pool = mp.Pool(processes=int(mp.cpu_count()))
     step = math.ceil(len(stocks) / mp.cpu_count())
+    # pool = mp.Pool(processes=16)
+    # step = math.ceil(len(stocks) / 16)
     # step = 10000
-    x = 1
     # tech_anal(stocks, hlfx_pool)
     for i in range(0, len(stocks), step):
-        print(x)
         pool.apply_async(func=tech_anal, args=(stocks[i:i + step], hlfx_pool, hlfx_pool_daily, err_list,),
                          error_callback=err_call_back)
-        x += 1
     time.sleep(5)
     pool.close()
     pool.join()
+    engine_hlfx_pool.dispose()
 
     print(f'当日信号:{len(hlfx_pool_daily)},持续检测为:{len(hlfx_pool)}')
     print(len(err_list), err_list)
@@ -303,11 +328,26 @@ if __name__ == '__main__':
                                port=3307,
                                password='r6kEwqWU9!v3',
                                database='hlfx_pool')
-    cursor_pool2 = db_pool.cursor()
+    cursor_pool2 = db_pool2.cursor()
     sql2 = "INSERT INTO daily_%s (date,value) VALUES('%s','%s')" % (fre, dt.now().strftime('%Y-%m-%d %H:%M:%S'),
                                                                     results_list_daily)
     cursor_pool2.execute(sql2)
     db_pool2.commit()
-    edtime = dt.now()
 
+    edtime = dt.now()
     print(edtime - sttime)
+
+
+if __name__ == '__main__':
+    pus = psutil.Process()
+    # pus.cpu_affinity([12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23])
+
+    # ind()
+
+    scheduler = BlockingScheduler()
+    scheduler.add_job(func=ind, trigger='cron', day_of_week='0-4', hour='20', minute='30',
+                      timezone="Asia/Shanghai", max_instances=10)
+    try:
+        scheduler.start()
+    except (KeyboardInterrupt, SystemExit):
+        pass

+ 170 - 128
QMT/qmt_real_hlfx.py

@@ -2,7 +2,7 @@
 # from jqdatasdk import *
 import pandas as pd
 import pymysql
-from sqlalchemy import create_engine
+from sqlalchemy import create_engine, text
 import threading
 from datetime import datetime as dt
 import datetime
@@ -17,9 +17,10 @@ import os
 import psutil
 import traceback
 from apscheduler.schedulers.blocking import BlockingScheduler
+import sys
+import gc
 
-#原始版本
-
+# 原始版本
 # auth('18616891214', 'Ea?*7f68nD.dafcW34d!')
 # auth('18521506014', 'Abc123!@#')
 # stocks = list(get_all_securities(['stock'], date=dt.today().strftime('%Y-%m-%d')).index)
@@ -28,20 +29,6 @@ from apscheduler.schedulers.blocking import BlockingScheduler
 pd.set_option('display.max_columns', None) # 设置显示最大行
 fre = '1d'
 
-def run(seq):
-    '''阻塞线程接收行情回调'''
-    import time
-    client = xtdata.get_client()
-    while True:
-        time.sleep(3)
-        now_date = dt.now()
-        if not client.is_connected() or dt.now() > now_date.replace(hour=15, minute=0, second=0):
-            xtdata.unsubscribe_quote(seq)
-            print(f'现在时间:{dt.now()},已收盘')
-            raise Exception('行情服务连接断开')
-            break
-    return
-
 
 class MyXtQuantTraderCallback(XtQuantTraderCallback):
     def on_disconnected(self):
@@ -113,32 +100,65 @@ def err_call_back(err):
     traceback.print_exc()
 
 
-def hlfx(data):
-    stock_list = list(data.keys())
-    print(f'def-->hlfx, MyPid is {os.getpid()}, 本次我需要计算{len(stock_list)},now is {dt.now()}')
+def run(seq):
+    mor = datetime.datetime.strptime(
+        str(dt.now().date()) + '11:30', '%Y-%m-%d%H:%M')
+    afternoon = datetime.datetime.strptime(
+        str(dt.now().date()) + '15:00', '%Y-%m-%d%H:%M')
+    mor_1 = datetime.datetime.strptime(
+        str(dt.now().date()) + '11:10', '%Y-%m-%d%H:%M')
+    """阻塞线程接收行情回调"""
+    import time
+    client = xtdata.get_client()
+    while True:
+        now_date = dt.now()
+        if not client.is_connected():
+            xtdata.unsubscribe_quote(seq)
+            raise Exception('行情服务连接断开')
+        # if mor < dt.now() < mor_1:
+        #     xtdata.unsubscribe_quote(seq)
+        #     print(f'现在时间:{dt.now()},已休市')
+        #     sys.exit()
+        #     break
+        #     return 0
+        elif dt.now() > afternoon:
+            xtdata.unsubscribe_quote(seq)
+            print(f'现在时间:{dt.now()},已收盘')
+            sys.exit()
+            break
+
+    return
+
+
+def hlfx(stock_list, data):
+    # stock_list = list(data.keys())
+    # print(f'def-->hlfx, MyPid is {os.getpid()}, 本次我需要计算{len(stock_list)},now is {dt.now()}')
 
     # 获得hlfx_pool池子
-    engine_hlfx_pool = create_engine('mysql+pymysql://root:r6kEwqWU9!v3@localhost:3307/hlfx_pool?charset=utf8')
+    engine_hlfx_pool = create_engine('mysql+pymysql://root:r6kEwqWU9!v3@localhost:3307/hlfx_pool?charset=utf8',
+                                     pool_size=100, pool_recycle=3600, max_overflow=50, pool_timeout=60)
     results = []
-    results.extend(pd.read_sql_query(
-        'select value from `%s`' % fre, engine_hlfx_pool).iloc[-1, 0].split(","))
-    print(f'本次hlfx_pool有{len(results)}个个股')
+    results.extend(pd.read_sql_query(text(
+        'select value from `%s` order by `index` desc limit 10' % fre), engine_hlfx_pool.connect()).iloc[0, 0].split(","))
+    # print(f'本次hlfx_pool有{len(results)}个个股')
 
-    engine_stock = create_engine('mysql+pymysql://root:r6kEwqWU9!v3@localhost:3307/qmt_stocks_tech?charset=utf8')
+    engine_stock = create_engine('mysql+pymysql://root:r6kEwqWU9!v3@localhost:3307/qmt_stocks_tech?charset=utf8',
+                                 pool_size=100, pool_recycle=3600, max_overflow=50, pool_timeout=60)
 
     for qmt_stock in stock_list:
         # 读取qmt_stocks_whole表-前复权-信息
         try:
-            df_day = pd.read_sql_query(
-                'select time, open_front, close_front, high_front, low_front, volume_front, amount_front, dif, dea, macd,HL from `%s_%s`'
-                % (qmt_stock, fre), engine_stock)
+            df_day = pd.read_sql_query(text(
+                'select time, open_front, close_front, high_front, low_front, volume_front, amount_front, '
+                'dif, dea, macd, HL from `%s_%s`' % (qmt_stock, fre)), engine_stock.connect())
             df_day.columns = ['time', 'open', 'close', 'high', 'low', 'volume', 'amount', 'dif', 'dea', 'macd', 'HL']
         except BaseException as e:
-            print(qmt_stock, '未能读取!', e)
+            print(qmt_stock, '未能读取!')
             pass
         else:
             # 获得最新价格信息
             get_price = data[qmt_stock]
+            # print(get_price)
             # 调整time时间格式
             get_price['time'] = dt.fromtimestamp(get_price['time'] / 1000.0)
             # print('成功判定', get_price['time'])
@@ -161,99 +181,102 @@ def hlfx(data):
 
             # 包含
             else:
-                # 左高,下降
-                if df_day.iloc[-2, 3] > df_day.iloc[-1, 3]:
-                    df_day.iloc[-1, 3] = min(df_day.iloc[-1, 3], get_price['high'])
-                    df_day.iloc[-1, 4] = min(df_day.iloc[-1, 4], get_price['low'])
-                # 右高,上升
-                else:
-                    df_day.iloc[-1, 3] = max(df_day.iloc[-1, 3], get_price['high'])
-                    df_day.iloc[-1, 4] = max(df_day.iloc[-1, 4], get_price['low'])
+                if len(df_day) > 2:
+                    # 左高,下降
+                    if df_day.iloc[-2, 3] > df_day.iloc[-1, 3]:
+                        df_day.iloc[-1, 3] = min(df_day.iloc[-1, 3], get_price['high'])
+                        df_day.iloc[-1, 4] = min(df_day.iloc[-1, 4], get_price['low'])
+                    # 右高,上升
+                    else:
+                        df_day.iloc[-1, 3] = max(df_day.iloc[-1, 3], get_price['high'])
+                        df_day.iloc[-1, 4] = max(df_day.iloc[-1, 4], get_price['low'])
                 # print('包含', df_day)
 
             # 数合并完成,确认df_day
             # print(df_day)
 
             # 寻找顶底分型
-            x = len(df_day.index)-1
-            m = x - 1
-            # 底
-            if ((df_day.loc[x, 'high'] > df_day.loc[x - 1, 'high']) and (
-                    df_day.loc[x - 2, 'high'] > df_day.loc[x - 1, 'high'])):
-                df_day.loc[x, 'HL'] = 'L*'
-                # 判断底的性质
-                while m:
-                    if df_day.loc[m, 'HL'] in ['H', 'HH', 'H*']:
-                        if (x - m) > 3:
-                            # 成笔——>L
-                            df_day.loc[x, 'HL'] = 'L'
-
-                    elif df_day.loc[m, 'HL'] == 'L':
-                        if df_day.loc[m - 1, 'low'] > df_day.loc[x - 1, 'low']:
-                            # pool_list.append(qmt_stock)
-
-                            # 获得MACD,判断MACD判断背驰
-                            x_macd_dif, x_macd_dea, x_macd_macd = df_day.loc[x, 'dif'], df_day.loc[x, 'dea'], \
-                            df_day.loc[x, 'macd']
-                            m_macd_dif, m_macd_dea, m_macd_macd = df_day.loc[m, 'dif'], df_day.loc[m, 'dea'], \
-                            df_day.loc[m, 'macd']
-
-                            # 背驰底->LL
-                            if m_macd_dif < x_macd_dif:
-                                df_day.loc[x, 'HL'] = 'LL'
-                                # 产生信号,进入hlfx_pool
-                                results.append(qmt_stock)
-                            # 前一个为底更高,且中间不存在更低的底
-                            else:
+            if len(df_day) > 2:
+                x = len(df_day.index)-1
+                m = x - 1
+                # 底
+                if ((df_day.loc[x, 'high'] > df_day.loc[x - 1, 'high']) and (
+                        df_day.loc[x - 2, 'high'] > df_day.loc[x - 1, 'high'])):
+                    df_day.loc[x, 'HL'] = 'L*'
+                    # 判断底的性质
+                    while m:
+                        if df_day.loc[m, 'HL'] in ['H', 'HH', 'H*']:
+                            if (x - m) > 3:
+                                # 成笔——>L
                                 df_day.loc[x, 'HL'] = 'L'
-                                # 产生信号,进入hlfx_pool
-                                results.append(qmt_stock)
-                            break
-                        break
-                    m = m - 1
-                    if m == 0:
-                        df_day.loc[x, 'HL'] = 'L'
-
-            # 顶
-
-            elif (df_day.loc[x, 'high'] < df_day.loc[x - 1, 'high']) and (
-                    df_day.loc[x - 2, 'high'] < df_day.loc[x - 1, 'high']) and (qmt_stock in results):
-                df_day.loc[x, 'HL'] = 'H*'
-                while m:
-                    if df_day.loc[m, 'HL'] in ['L', 'LL', 'L*']:
-                        if x - m > 3:
-                            # 成笔->H
-                            df_day.loc[x, 'HL'] = 'H'
-                            # 产生卖出信号,进入hlfx_pool
-                            results.remove(qmt_stock)
-                            break
+                                break
 
-                    elif (df_day.loc[m, 'HL'] == 'H'):
-                        if df_day.loc[x - 1, 'high'] > df_day.loc[m - 1, 'high']:
-                            # 获得MACD,判断MACD判断背驰
-                            x_macd_dif, x_macd_dea, x_macd_macd = df_day.loc[x, 'dif'], df_day.loc[x, 'dea'], \
+                        elif df_day.loc[m, 'HL'] in ['L', 'LL', 'L*']:
+                            if df_day.loc[m - 1, 'low'] > df_day.loc[x - 1, 'low']:
+                                # pool_list.append(qmt_stock)
+
+                                # 获得MACD,判断MACD判断背驰
+                                x_macd_dif, x_macd_dea, x_macd_macd = df_day.loc[x, 'dif'], df_day.loc[x, 'dea'], \
                                 df_day.loc[x, 'macd']
-                            m_macd_dif, m_macd_dea, m_macd_macd = df_day.loc[m, 'dif'], df_day.loc[m, 'dea'], \
+                                m_macd_dif, m_macd_dea, m_macd_macd = df_day.loc[m, 'dif'], df_day.loc[m, 'dea'], \
                                 df_day.loc[m, 'macd']
 
-                            # MACD顶背驰
-                            if x_macd_dif < m_macd_dif:
-                                df_day.loc[x, 'HL'] = 'HH'
-                                # 产生卖出信号,进入hlfx_pool
-                                results.remove(qmt_stock)
+                                # 背驰底->LL
+                                if m_macd_dif < x_macd_dif:
+                                    df_day.loc[x, 'HL'] = 'LL'
+                                    # 产生信号,进入hlfx_pool
+                                    results.append(qmt_stock)
+                                    break
+                                # 前一个为底更高,且中间不存在更低的底
+                                else:
+                                    df_day.loc[x, 'HL'] = 'L'
+                                    # 产生信号,进入hlfx_pool
+                            break
+                        m = m - 1
+                        if m == 0:
+                            df_day.loc[x, 'HL'] = 'L'
+                            results.append(qmt_stock)
+
+                # 顶
 
-                            # 前一个为顶,且中间存在不包含 or 更高的顶
-                            else:
+                elif (df_day.loc[x, 'high'] < df_day.loc[x - 1, 'high']) and (
+                        df_day.loc[x - 2, 'high'] < df_day.loc[x - 1, 'high']) and (qmt_stock in results):
+                    df_day.loc[x, 'HL'] = 'H*'
+                    while m:
+                        if df_day.loc[m, 'HL'] in ['L', 'LL', 'L*']:
+                            if x - m > 3:
+                                # 成笔->H
                                 df_day.loc[x, 'HL'] = 'H'
                                 # 产生卖出信号,进入hlfx_pool
                                 results.remove(qmt_stock)
-
+                                break
+
+                        elif df_day.loc[m, 'HL'] in ['H','HH', 'H*']:
+                            if df_day.loc[x - 1, 'high'] > df_day.loc[m - 1, 'high']:
+                                # 获得MACD,判断MACD判断背驰
+                                x_macd_dif, x_macd_dea, x_macd_macd = df_day.loc[x, 'dif'], df_day.loc[x, 'dea'], \
+                                    df_day.loc[x, 'macd']
+                                m_macd_dif, m_macd_dea, m_macd_macd = df_day.loc[m, 'dif'], df_day.loc[m, 'dea'], \
+                                    df_day.loc[m, 'macd']
+
+                                # MACD顶背驰
+                                if x_macd_dif < m_macd_dif:
+                                    df_day.loc[x, 'HL'] = 'HH'
+                                    # 产生卖出信号,进入hlfx_pool
+                                    results.remove(qmt_stock)
+                                    break
+
+                                # 前一个为顶,且中间存在不包含 or 更高的顶
+                                else:
+                                    df_day.loc[x, 'HL'] = 'H'
+                                    # 产生卖出信号,进入hlfx_pool
+                                    results.remove(qmt_stock)
                             break
-                        break
-                    m = m - 1
-                    if m == 0:
-                        df_day.loc[x, 'HL'] = 'H'
-    engine_stock.dispose()
+                        m = m - 1
+                        if m == 0:
+                            df_day.loc[x, 'HL'] = 'H'
+                            results.remove(qmt_stock)
+
 
     db_pool = pymysql.connect(host='localhost',
                               user='root',
@@ -261,44 +284,59 @@ def hlfx(data):
                               password='r6kEwqWU9!v3',
                               database='hlfx_pool')
     cursor_pool = db_pool.cursor()
-    # print(set(results))
+
     results_list = ','.join(set(results))
     sql = "INSERT INTO %s (date,value) VALUES('%s','%s')" % (fre, dt.now().strftime('%Y-%m-%d %H:%M:%S'), results_list)
     cursor_pool.execute(sql)
     db_pool.commit()
-    print(f'{dt.now()}写入新的results{len(results_list)}个,hlfx_pool更新')
+    print(f'{dt.now()} 新的results有{len(set(results))}, \n {set(results)}')
+    engine_stock.dispose()
     engine_hlfx_pool.dispose()
 
 
+def prepare(data):
+    stock_list = list(data.keys())
+    if len(data.keys()) >= 12:
+        cpu_count = 12
+    else:
+        cpu_count = len(data.keys())
+    step = math.ceil(len(stock_list) / cpu_count)
 
-def bridge(list):
-    print(f'MyPid is {os.getpid()}, now is {dt.now()},我需要负责{len(list)}个个股数据')
-    seq = xtdata.subscribe_whole_quote(list, callback=hlfx)
-    run(seq)
+    to_hlfx_list = []
+    for i in range(0, len(stock_list), step):
+        to_hlfx_list.append([x for x in stock_list[i:i + step]])
+
+    pool = mp.Pool(processes=cpu_count, maxtasksperchild=12)
+    for m in range(len(to_hlfx_list)):
+        pool.apply_async(func=hlfx,
+                         args=(to_hlfx_list[m], data), error_callback=err_call_back)
+    pool.close()
+    pool.join()
 
 
-def prepare():
+def bridge():
+    print(f'bridge is {os.getpid()}, now is {dt.now()},开盘了')
     stocks = xtdata.get_stock_list_in_sector('沪深A股')
+    seq = xtdata.subscribe_whole_quote(stocks, callback=prepare)
+    run(seq)
 
-    cpu_count = 4
-    pool = mp.Pool(processes=cpu_count, maxtasksperchild=8)
-    step = math.ceil(len(stocks) / cpu_count)
-    to_hlfx_list = []
 
-    for i in range(0, len(stocks), step):
-        to_hlfx_list.append([x for x in stocks[i:i + step]])
-
-    for m in range(cpu_count):
-        pool.apply_async(func=bridge,
-                         args=(to_hlfx_list[m],), error_callback=err_call_back)
-    pool.close()
-    pool.join()
+def job_func():
+    print(f"Job started at {dt.now()}")
+    # 创建子进程
+    p = mp.Process(target=bridge)
+    # 启动子进程
+    p.start()
+    # 等待子进程结束
+    p.join()
+    print(f"Job finished at {dt.now()}")
 
 
 if __name__ == '__main__':
+    print(f'总进程pid:{os.getpid()}')
     mp.freeze_support()
     pus = psutil.Process()
-    pus.cpu_affinity([0, 1, 2, 3])
+    pus.cpu_affinity([0, 1, 2, 3, 4, 5, 6, 7])
 
     path = r'c:\\qmt\\userdata_mini'
     # 生成session id 整数类型 同时运行的策略不能重复
@@ -318,9 +356,13 @@ if __name__ == '__main__':
     subscribe_result = xt_trader.subscribe(acc)
     print('对交易回调进行订阅,订阅后可以收到交易主推,返回0表示订阅成功', subscribe_result)
 
+    # job_func()
+
     scheduler = BlockingScheduler()
-    scheduler.add_job(func=prepare, trigger='cron', day_of_week='0-4', hour='9', minute='25',
-                      timezone="Asia/Shanghai")
+    scheduler.add_job(func=job_func, trigger='cron', day_of_week='0-4', hour='09', minute='25',
+                      timezone="Asia/Shanghai", max_instances=5)
+    # # scheduler.add_job(func=job_func, trigger='cron', day_of_week='0-4', hour='13', minute='00',
+    # #                   timezone="Asia/Shanghai")
     try:
         scheduler.start()
     except (KeyboardInterrupt, SystemExit):

+ 190 - 152
QMT/real_time.py

@@ -12,31 +12,130 @@ import pymysql
 import multiprocessing as mp
 import math
 import psutil
+import datetime
 from apscheduler.schedulers.blocking import BlockingScheduler
+import sys
+import gc
 
-auth('18616891214', 'Ea?*7f68nD.dafcW34d!')
-db_pool = pymysql.connect(host='localhost',
-                          user='root',
-                          port=3307,
-                          password='r6kEwqWU9!v3',
-                          database='hlfx_pool')
-cursor_pool = db_pool.cursor()
+# auth('18616891214', 'Ea?*7f68nD.dafcW34d!')
 engine_stock = create_engine('mysql+pymysql://root:r6kEwqWU9!v3@localhost:3307/qmt_stocks_whole?charset=utf8')
+auth('18616891214', 'Ea?*7f68nD.dafcW34d!')
+
+
+class MyXtQuantTraderCallback(XtQuantTraderCallback):
+    def on_disconnected(self):
+        """
+        连接断开
+        :return:
+        """
+        print(datetime.datetime.now(), '连接断开回调')
+
+    def on_stock_order(self, order):
+        """
+        委托回报推送
+        :param order: XtOrder对象
+        :return:
+        """
+        print(datetime.datetime.now(), '委托回调', order.order_remark)
+
+    def on_stock_trade(self, trade):
+        """
+        成交变动推送
+        :param trade: XtTrade对象
+        :return:
+        """
+        print(datetime.datetime.now(), '成交回调', trade.order_remark)
+
+    def on_order_error(self, order_error):
+        """
+        委托失败推送
+        :param order_error:XtOrderError 对象
+        :return:
+        """
+        # print("on order_error callback")
+        # print(order_error.order_id, order_error.error_id, order_error.error_msg)
+        print(f"委托报错回调 {order_error.order_remark} {order_error.error_msg}")
+
+    def on_cancel_error(self, cancel_error):
+        """
+        撤单失败推送
+        :param cancel_error: XtCancelError 对象
+        :return:
+        """
+        print(datetime.datetime.now(), sys._getframe().f_code.co_name)
+
+    def on_order_stock_async_response(self, response):
+        """
+        异步下单回报推送
+        :param response: XtOrderResponse 对象
+        :return:
+        """
+        print(f"异步委托回调 {response.order_remark}")
+
+    def on_cancel_order_stock_async_response(self, response):
+        """
+        :param response: XtCancelOrderResponse 对象
+        :return:
+        """
+        print(datetime.datetime.now(), sys._getframe().f_code.co_name)
+
+    def on_account_status(self, status):
+        """
+        :param response: XtAccountStatus 对象
+        :return:
+        """
+        print(datetime.datetime.now(), sys._getframe().f_code.co_name)
 
 
-def run(seq):
-    print(seq)
-    '''阻塞线程接收行情回调'''
+# 指定客户端所在路径
+path = r'c:\\qmt\\userdata_mini'
+# 生成session id 整数类型 同时运行的策略不能重复
+session_id = int(time.time())
+xt_trader = XtQuantTrader(path, session_id)
+# 创建资金账号为 800068 的证券账号对象
+acc = StockAccount('920000207040', 'SECURITY')
+# 创建交易回调类对象,并声明接收回调
+callback = MyXtQuantTraderCallback()
+xt_trader.register_callback(callback)
+# 启动交易线程
+xt_trader.start()
+# 建立交易连接,返回0表示连接成功
+connect_result = xt_trader.connect()
+print('建立交易连接,返回0表示连接成功', connect_result)
+# 对交易回调进行订阅,订阅后可以收到交易主推,返回0表示订阅成功
+subscribe_result = xt_trader.subscribe(acc)
+print('对交易回调进行订阅,订阅后可以收到交易主推,返回0表示订阅成功', subscribe_result)
+
+
+def run(seq, pid):
+    mor = datetime.datetime.strptime(
+        str(dt.now().date()) + '11:30', '%Y-%m-%d%H:%M')
+    afternoon = datetime.datetime.strptime(
+        str(dt.now().date()) + '15:00', '%Y-%m-%d%H:%M')
+    mor_1 = datetime.datetime.strptime(
+        str(dt.now().date()) + '12:59', '%Y-%m-%d%H:%M')
+    """阻塞线程接收行情回调"""
     import time
     client = xtdata.get_client()
     while True:
         time.sleep(3)
         now_date = dt.now()
-        if not client.is_connected() or dt.now() > now_date.replace(hour=13, minute=26, second=0):
+        if not client.is_connected():
             xtdata.unsubscribe_quote(seq)
             raise Exception('行情服务连接断开')
+        # if mor < dt.now() < mor_1:
+        #     xtdata.unsubscribe_quote(seq)
+        #     print(f'现在时间:{dt.now()},已休市')
+        #     sys.exit()
+        #     break
+        #     return 0
+        elif dt.now() > afternoon:
+            xtdata.unsubscribe_quote(seq)
+            print(f'现在时间:{dt.now()},已收盘')
+            sys.exit()
             break
-    return
+            # return 0
+    # return
 
 
 def real_price(datas):
@@ -57,13 +156,13 @@ def ma(stock, num, data):
     except:
         return 9999999
     else:
-        ma_num = (sum(df['close_front'][i:]) + data[stock]['lastPrice'])/num
+        ma_num = (sum(df['close_front'][i:]) + data[stock]['lastPrice']) / num
         return ma_num
 
 
 def ma_1(stock, num):
     global engine_stock
-    i = (num) * -1
+    i = num * -1
     try:
         df = pd.read_sql_query(
             'select close_front from `%s_1d`' % stock, engine_stock)
@@ -86,53 +185,61 @@ def his_vol(stock, num):
         return df['volume_front'].iloc[num]
 
 
-def ma_judge(data, stock_list, rate, results):
+def ma_judge(data, list_judge, rate, results):
     # print(f',收到的data数据为:{len(data.keys())},stock_pool长度为{len(stock_list)},now is {dt.now()}')
-    list_judge = list(set(data.keys()) & set(stock_list))
+
     print(f'这个ma_judge的PID为:{os.getpid()},本轮计算:{len(list_judge)}个股')
     for stock in list_judge:
-        i = stock.replace('XSHG', 'SH').replace('XSHE', 'SZ')
-        current_price, open_price = data[i]['lastPrice'], data[i]['open']
-        MA5, MA10, MA20, MA30, MA60, MA120 = ma(i, 5, data), ma(i, 10, data), ma(i, 20, data), ma(i, 30, data),\
-            ma(i, 60, data), ma(i, 120, data)
-        MA5_1 = ma_1(i, 5)
+        current_price, open_price = data[stock]['lastPrice'], data[stock]['open']
+        MA5, MA10, MA20, MA30, MA60, MA120 = ma(stock, 5, data), ma(stock, 10, data), ma(stock, 20, data), ma(stock, 30,
+                                                                                                              data), \
+            ma(stock, 60, data), ma(stock, 120, data)
+        MA5_1 = ma_1(stock, 5)
         # print(i, current_price, open_price, MA5, MA10, MA20, MA5_1)
         # 入交易池标准:阳线\大于MA5\MA5向上\MA20<MA10\离120线有距离
         if (current_price > open_price) & (current_price > MA5) & (MA5 > MA5_1) & (current_price < MA5 * 1.03) & (
-                MA20 < MA10) & (current_price > MA120 or current_price < MA120*rate):
-            if his_vol(i, -1) > his_vol(i, -2):
-                results.append(i.replace('SH', 'XSHG').replace('SZ', 'XSHE'))
+                MA20 < MA10) & (current_price > MA120 or current_price < MA120 * rate):
+            if his_vol(stock, -1) > his_vol(stock, -2):
+                results.append(stock.replace('SH', 'XSHG').replace('SZ', 'XSHE'))
 
 
-def sell_trader(data, positions_dict):
-    # for m in data:
-    #     print(m, data[m]['lastPrice'])
+def get_fundamentals(results):
+    return results
+    pass
+
+
+def sell_trader(data):
     print('卖出函数:', dt.now())
     positions = xt_trader.query_stock_positions(acc)
-    print('持仓总数:', len(positions))
+    positions_dict = {positions[x].stock_code: positions[x].can_use_volume for x in range(0, len(positions))}
+    print(f'今日可卖出个股总数:{len([value for value in positions_dict.values() if value != 0])}')
+    print(
+        f'目前持仓总数为:{len([positions[x].stock_code for x in range(0, len(positions)) if positions[x].volume != 0])}')
 
-    for stock, volume in positions_dict.items():
-        if stock in data:
+    for stock, can_use_volume in positions_dict.items():
+        if stock in data and can_use_volume != 0:
             current_price = data[stock]['lastPrice']
             open_price = data[stock]['open']
             MA5 = ma(stock, 5, data)
             MA5_1 = ma_1(stock, 5)
-            print(f'{stock},持仓量为{volume}当前价:{current_price},MA5:{MA5},昨日MA5:{MA5_1},开始判断:')
-            if current_price < MA5 or MA5 < MA5_1 or current_price > MA5 * 1.07:
+            print(f'{stock},持仓量为{can_use_volume}当前价:{current_price},MA5:{MA5},昨日MA5:{MA5_1},开始判断:')
+            if current_price < MA5 or MA5 < MA5_1:
                 print('卖出信号!!!!!!', stock, current_price)
-                order_id = xt_trader.order_stock(acc, stock, xtconstant.STOCK_SELL, volume,
-                                                 xtconstant.LATEST_PRICE, 0, 'strategy1', 'order_test')
+                order_id = xt_trader.order_stock(acc, stock, xtconstant.STOCK_SELL, can_use_volume,
+                                                 xtconstant.LATEST_PRICE, 0, 'MA5策略', '低于MA5趋势向下')
                 print('价格:', current_price, open_price, MA5, MA5_1)
-                print(order_id, stock, volume)
+                print(order_id, stock, can_use_volume)
+            elif current_price > MA5 * 1.07:
+                print('盈利乖离率超7%!!!!!!', stock, current_price)
+                order_id = xt_trader.order_stock(acc, stock, xtconstant.STOCK_SELL, can_use_volume,
+                                                 xtconstant.LATEST_PRICE, 0, 'MA5策略', '盈利乖离率超7%')
+                print('价格:', current_price, open_price, MA5, MA5_1)
+                print(order_id, stock, can_use_volume)
         else:
             print(f'本轮没有持仓股票信息!')
 
 
-def get_fundamentals(results):
-    return results
-    pass
-
-def buy_trader(data, positions):
+def buy_trader(data):
     print('买入函数:', dt.now(), f'接受到{len(data.keys())}个个股')
     results = mp.Manager().list()
     mp_list = []
@@ -140,10 +247,10 @@ def buy_trader(data, positions):
 
     try:
         stock_pool = pd.read_sql_query(
-            'select value from `%s`' % '1d', engine_hlfx_pool)
-        stock_pool = stock_pool.iloc[-1, 0].split(",")
+            'select value from `%s` order by `index` desc limit 10' % '1d', engine_hlfx_pool)
+        stock_pool = stock_pool.iloc[0, 0].split(",")
         stock_pool.sort()
-        print('stock_pool',len(stock_pool))
+        print('stock_pool', len(stock_pool))
     except BaseException:
         pass
     '''
@@ -159,12 +266,13 @@ def buy_trader(data, positions):
                     results.append(stock.replace('SH', 'XSHG').replace('SZ', 'XSHE'))
                     print('append')
     '''
-
-    step = math.ceil(len(stock_pool) / (mp.cpu_count()/2))
+    list_judge = list(set(data.keys()) & set(stock_pool))
+    print(f'本轮有{len(data.keys())}条个股信息,而list_judge有:{len(list_judge)}')
+    step = math.ceil(len(list_judge) / 2)
     print('step:', step)
     rate = 0.8
-    for i in range(0, len(stock_pool), step):
-        p = mp.Process(target=ma_judge, args=(data, stock_pool[i:i + step], rate, results))
+    for i in range(0, len(list_judge), step):
+        p = mp.Process(target=ma_judge, args=(data, list_judge[i:i + step], rate, results))
         mp_list.append(p)
         p.start()
     for j in mp_list:
@@ -192,6 +300,12 @@ def buy_trader(data, positions):
         results_industry = ','.join(set(results_industry))
         print('1d', '\n', results_industry)
 
+        db_pool = pymysql.connect(host='localhost',
+                                  user='root',
+                                  port=3307,
+                                  password='r6kEwqWU9!v3',
+                                  database='hlfx_pool')
+        cursor_pool = db_pool.cursor()
         sql = "INSERT INTO MA5_%s (date,value) VALUES('%s','%s')" % ('1d', dt.now().strftime('%Y-%m-%d %H:%M:%S'),
                                                                      results_industry)
         cursor_pool.execute(sql)
@@ -199,18 +313,21 @@ def buy_trader(data, positions):
 
         # print(len(results_industry), results_industry)
         print(dt.now(), '数据库数据已赋值!')
+        cursor_pool.close()
+        db_pool.close()
 
         # 取值交易
-
         keep_stocks = results_industry.split(",")
         new_keep_stock = [stock.replace('XSHG', 'SH').replace('XSHE', 'SZ') for stock in keep_stocks]
         print(f'new_keep_stock is:{len(new_keep_stock)}')
 
-
-        #进入购买程序
+        # 进入购买程序
         max_pos = 7
         for stock in new_keep_stock:
+            positions = xt_trader.query_stock_positions(acc)
+
             asset = xt_trader.query_stock_asset(acc)
+            print('bbbb', positions, asset)
             cash = asset.cash
             positions_dict = {positions[x].stock_code: positions[x].volume for x in range(0, len(positions)) if
                               positions[x].volume > 0}
@@ -218,138 +335,59 @@ def buy_trader(data, positions):
             current_price = data[stock]['lastPrice']
             current_high = data[stock]['high']
             if cash > 5000 and len(positions_dict) < max_pos and current_price > 9 \
-                    and current_price > (current_high*0.98):
+                    and current_price > (current_high * 0.98):
                 volume = int((cash / 3 / current_price) // 100 * 100)
                 print('买入信号!!!!!!', stock, volume, current_price)
                 order_id = xt_trader.order_stock(acc, stock, xtconstant.STOCK_BUY, volume, xtconstant.LATEST_PRICE,
-                                                 current_price, 'strategy1', 'order_test')
+                                                 current_price, 'MA5策略', 'MA5趋势向上')
                 print(order_id)
             else:
-                print(f'Cash只有:{cash} 或者 现有持仓{len(positions)} 超过了{max_pos}')
+                print(f'Cash只有:{cash} 或者 现有持仓{len(positions_dict)} 超过了{max_pos}')
     engine_hlfx_pool.dispose()
     print('一轮结束了,现在时间是:', dt.now())
 
 
 def trader(data):
-    print(len(data.keys()))
-
-    # 先判断卖出条件
-    positions = xt_trader.query_stock_positions(acc)
-    print('持仓数量', len(positions))
-    if len(positions) != 0:
-        positions_dict = {positions[x].stock_code: positions[x].volume for x in range(0, len(positions))}
-        sell_trader(data, positions_dict)
-
+    sell_trader(data)
     # 买入条件
-    buy_trader(data, positions)
+    buy_trader(data)
 
 
 def bridge():
-    print("start")
+    pid = os.getpid()
+    print(f'MyPid is {os.getpid()}, now is {dt.now()},开盘了')
     stocks = xtdata.get_stock_list_in_sector('沪深A股')
     seq = xtdata.subscribe_whole_quote(stocks, callback=trader)
-    run(seq)
-
-
-class MyXtQuantTraderCallback(XtQuantTraderCallback):
-    def on_disconnected(self):
-        """
-        连接断开
-        :return:
-        """
-        print(datetime.datetime.now(), '连接断开回调')
-
-    def on_stock_order(self, order):
-        """
-        委托回报推送
-        :param order: XtOrder对象
-        :return:
-        """
-        print(datetime.datetime.now(), '委托回调', order.order_remark)
-
-    def on_stock_trade(self, trade):
-        """
-        成交变动推送
-        :param trade: XtTrade对象
-        :return:
-        """
-        print(datetime.datetime.now(), '成交回调', trade.order_remark)
-
-    def on_order_error(self, order_error):
-        """
-        委托失败推送
-        :param order_error:XtOrderError 对象
-        :return:
-        """
-        # print("on order_error callback")
-        # print(order_error.order_id, order_error.error_id, order_error.error_msg)
-        print(f"委托报错回调 {order_error.order_remark} {order_error.error_msg}")
-
-    def on_cancel_error(self, cancel_error):
-        """
-        撤单失败推送
-        :param cancel_error: XtCancelError 对象
-        :return:
-        """
-        print(datetime.datetime.now(), sys._getframe().f_code.co_name)
-
-    def on_order_stock_async_response(self, response):
-        """
-        异步下单回报推送
-        :param response: XtOrderResponse 对象
-        :return:
-        """
-        print(f"异步委托回调 {response.order_remark}")
+    run(seq, pid)
 
-    def on_cancel_order_stock_async_response(self, response):
-        """
-        :param response: XtCancelOrderResponse 对象
-        :return:
-        """
-        print(datetime.datetime.now(), sys._getframe().f_code.co_name)
 
-    def on_account_status(self, status):
-        """
-        :param response: XtAccountStatus 对象
-        :return:
-        """
-        print(datetime.datetime.now(), sys._getframe().f_code.co_name)
+def job_func():
+    print(f"Job started at {dt.now()}")
+    # 创建子进程
+    p = mp.Process(target=bridge)
+    # 启动子进程
+    p.start()
+    # 等待子进程结束
+    p.join()
+    print(f"Job finished at {dt.now()}")
 
 
 if __name__ == '__main__':
-    auth('18616891214', 'Ea?*7f68nD.dafcW34d!')
-
     mp.freeze_support()
     print('cpu_count =', mp.cpu_count())
     pus = psutil.Process()
-    pus.cpu_affinity([4, 5, 6, 7])
-
-    # 指定客户端所在路径
-    path = r'c:\\qmt\\userdata_mini'
-    # 生成session id 整数类型 同时运行的策略不能重复
-    session_id = int(time.time())
-    xt_trader = XtQuantTrader(path, session_id)
-    # 创建资金账号为 800068 的证券账号对象
-    acc = StockAccount('920000207040', 'SECURITY')
-    # 创建交易回调类对象,并声明接收回调
-    callback = MyXtQuantTraderCallback()
-    xt_trader.register_callback(callback)
-    # 启动交易线程
-    xt_trader.start()
-    # 建立交易连接,返回0表示连接成功
-    connect_result = xt_trader.connect()
-    print('建立交易连接,返回0表示连接成功', connect_result)
-    # 对交易回调进行订阅,订阅后可以收到交易主推,返回0表示订阅成功
-    subscribe_result = xt_trader.subscribe(acc)
-    print('对交易回调进行订阅,订阅后可以收到交易主推,返回0表示订阅成功', subscribe_result)
+    pus.cpu_affinity([12, 13, 14, 15, 16, 17])
+
+    # job_func()
 
     scheduler = BlockingScheduler()
-    scheduler.add_job(func=bridge, trigger='cron', day_of_week='0-4', hour='9', minute='40',
+    scheduler.add_job(func=job_func, trigger='cron', day_of_week='0-4', hour='09', minute='40',
                       timezone="Asia/Shanghai")
+    # scheduler.add_job(func=job_func, trigger='cron', day_of_week='0-4', hour='13', minute='05',
+    #                   timezone="Asia/Shanghai")
     try:
         scheduler.start()
     except (KeyboardInterrupt, SystemExit):
         pass
 
-
     # xtdata.subscribe_quote('000001.SZ', '1d', '', '', count=1, callback=MA)

+ 81 - 0
QMT/test_kzz.py

@@ -0,0 +1,81 @@
+# coding:utf-8
+import pandas as pd
+import numpy as np
+from sklearn.preprocessing import MinMaxScaler
+from keras.models import Sequential
+from keras.layers import Dense, LSTM
+from keras.callbacks import EarlyStopping
+import matplotlib.pyplot as plt
+from sqlalchemy import create_engine
+
+# 加载数据
+engine_tech = create_engine('mysql+pymysql://root:r6kEwqWU9!v3@localhost:3307/qmt_stocks_tech?charset=utf8')
+
+
+df = pd.read_sql_table('600000.SH_1d', con=engine_tech)
+print(df)
+
+# 计算MACD指标
+ema12 = df['close_back'].ewm(span=12, adjust=False).mean()
+ema26 = df['close_back'].ewm(span=26, adjust=False).mean()
+macd = ema12 - ema26
+signal = macd.ewm(span=9, adjust=False).mean()
+df['macd'] = macd
+df['signal'] = signal
+
+# 准备数据
+data = df[['close_back', 'macd', 'signal', 'willr', 'volume_back', 'dif', 'dea', 'rsi_6', 'rsi_12', 'rsi_24']].values
+print(data)
+data = data[~np.isnan(data).any(axis=1), :]
+print('处理后', data)
+
+
+scaler = MinMaxScaler(feature_range=(0, 1))
+data = scaler.fit_transform(data)
+train_size = int(len(data) * 0.7)
+train_data = data[:train_size, :]
+test_data = data[train_size:, :]
+
+def create_dataset(dataset, look_back):
+    X, Y = [], []
+    for i in range(len(dataset) - look_back - 1):
+        X.append(dataset[i:(i + look_back), :])
+        Y.append(dataset[i + look_back, 0])
+    return np.array(X), np.array(Y)
+
+look_back = 90
+train_X, train_Y = create_dataset(train_data, look_back)
+test_X, test_Y = create_dataset(test_data, look_back)
+
+# LSTM模型
+model = Sequential()
+model.add(LSTM(units=50, input_shape=(train_X.shape[1], train_X.shape[2])))
+model.add(Dense(units=1))
+model.compile(optimizer='adam', loss='mean_squared_error')
+
+# 训练模型
+early_stop = EarlyStopping(monitor='val_loss', patience=10)
+history = model.fit(train_X, train_Y, epochs=100, batch_size=1, validation_data=(test_X, test_Y), callbacks=[early_stop], verbose=2)
+
+# 绘制损失函数
+plt.plot(history.history['loss'], label='train')
+plt.plot(history.history['val_loss'], label='test')
+plt.legend()
+plt.show()
+
+# 预测未来30天的涨跌
+last_data = data[-look_back:, :]
+last_data = np.expand_dims(last_data, axis=0)
+predictions = []
+for i in range(30):
+    prediction = model.predict(last_data)
+    predictions.append(prediction)
+    last_data = np.concatenate([last_data[:, 1:, :], prediction.reshape(1, 1, 3)], axis=1)
+predictions = scaler.inverse_transform(np.array(predictions).reshape(-1, 1))
+
+# 绘制预测结果
+plt.plot(predictions, label='predicted')
+plt.plot(df['back_close'].tail(30).reset_index(drop=True), label='actual')
+plt.legend()
+plt.show()
+

+ 17 - 11
backtrader/230202_backtrader.py

@@ -12,6 +12,8 @@ import math
 from datetime import datetime as dt
 import multiprocessing as mp
 from backtrader.feeds import PandasData
+import platform
+import psutil
 
 
 # import multiprocessing
@@ -151,18 +153,22 @@ def err_call_back(err):
 
 
 def to_df(lt):
-    df = pd.DataFrame(list(lt), columns=['周期', '波动率', '乖离率', '盈利个数', '盈利比例', '总盈利', '平均盈利', '最大盈利',
+    print('开始存数据')
+    df = pd.DataFrame(list(lt), columns=['周期', '波动率', 'MA5斜率', '盈利个数', '盈利比例', '总盈利', '平均盈利', '最大盈利',
                                '最小盈利', '总亏损', '平均亏损', '最大亏损', '最小亏损'])
-    df.sort_values(by=['周期', '波动率', '乖离率'], ascending=True, inplace=True)
+    df.sort_values(by=['周期', '波动率', 'MA5斜率'], ascending=True, inplace=True)
     df = df.reset_index(drop=True)
-    df.to_csv(f"D:\Daniel\策略\策略穷举{dt.now().strftime('%Y%m%d')}.csv", index=True, encoding='utf-8', mode='w')
-    print(df)
+    if platform.node() == 'DanieldeMBP.lan':
+        df.to_csv(f"/Users/daniel/Documents/策略/策略穷举{dt.now().strftime('%Y%m%d%H%m%S')}.csv", index=True, encoding='utf_8_sig', mode='w')
+    else:
+        df.to_csv(f"C:\Daniel\策略\策略穷举{dt.now().strftime('%Y%m%d%H%m%S' )}.csv", index=True, encoding='utf_8_sig', mode='w')
+    print(f'结果:{df}')
 
 
 def backtrader(list_date, table_list, result, result_change, result_change_fall, num, Volatility, rate, err_list):
     print(f'{num}天波动率为{Volatility}%乖离率为{rate}', 'myPID is ', os.getpid())
     sttime = dt.now()
-    engine = create_engine('mysql+pymysql://root:r6kEwqWU9!v3@localhost:3307/qmt_stocks_tech?charset=utf8')
+    engine = create_engine('mysql+pymysql://root:r6kEwqWU9!v3@localhost:3307/qmt_stocks_whole?charset=utf8')
     for stock in table_list:
         # print(stock)
         stk_df = pd.read_sql_table(stock, engine)
@@ -207,12 +213,12 @@ def backtrader(list_date, table_list, result, result_change, result_change_fall,
                 err_list.append(stock)
             else:
                 if cerebro.broker.getvalue() > 100000.0:
-                    result_change.append((cerebro.broker.getvalue() / 10000 - 1))
+                    result_change.append((cerebro.broker.getvalue() / 100000 - 1))
                     result.append(stock)
                     # print('recode!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!')
                     # print(result)
                 else:
-                    result_change_fall.append((1 - cerebro.broker.getvalue() / 10000))
+                    result_change_fall.append((1 - cerebro.broker.getvalue() / 100000))
                     # print('aaaaaaaaaaa')
                     # print(result_change_fall)
 
@@ -252,18 +258,18 @@ if __name__ == '__main__':
                          user='root',
                          port=3307,
                          password='r6kEwqWU9!v3',
-                         database='qmt_stocks')
+                         database='qmt_stocks_whole')
     cursor = db.cursor()
     cursor.execute("show tables like '%%%s%%' " % fre)
     table_list = [tuple[0] for tuple in cursor.fetchall()]
     # print(table_list)
-    # table_list = table_list[0:100]
+    table_list = table_list[0:500]
 
     list_date = mp.Manager().list()
     thread_list = []
     pool = mp.Pool(processes=mp.cpu_count())
-    for num in range(60, 100, 20):
-        for Volatility in range(5, 7, 1):
+    for num in range(60, 80, 20):
+        for Volatility in range(5, 8, 1):
             for rate in range(7, 9, 1):
                 step = math.ceil(len(table_list) / mp.cpu_count())
                 result = []

+ 3 - 3
backtrader/230215_backtrader.py

@@ -128,7 +128,7 @@ class TestStrategy(bt.Strategy):
                     and (((lowest * (1 - vola)) < self.low[-2] < (lowest * (1 + vola))) or (
                     (lowest * (1 - vola)) < self.low[-1] < (lowest * (1 + vola)))):
                 self.order = self.buy()
-            elif self.hl[0] == 5 and ((highest * (1 - vola)) < self.high[-2] < (highest * (1 + vola))):
+            elif self.hl[0] == 5 or ((highest * (1 - vola)) < self.high[-2] < (highest * (1 + vola))):
                 self.order = self.close()
         '''
                 if len(self) > self.params.num:
@@ -279,11 +279,11 @@ if __name__ == '__main__':
     cursor.execute("show tables like '%%%s%%' " % fre)
     table_list = [tuple[0] for tuple in cursor.fetchall()]
     # print(table_list)
-    # table_list = table_list[0:100]
+    table_list = table_list[0:100]
 
     list_date = mp.Manager().list()
     thread_list = []
-    pool = mp.Pool(processes=mp.cpu_count())
+    pool = mp.Pool(processes=int(mp.cpu_count()/2))
     for num in range(60, 180, 20):
         for Volatility in range(5, 8, 1):
             for rate in range(7, 8, 1):

+ 2 - 2
backtrader/230219_backtrader.py

@@ -187,8 +187,8 @@ def to_df(lt):
                                '最小盈利', '总亏损', '平均亏损', '最大亏损', '最小亏损'])
     df.sort_values(by=['周期', '波动率', '量能增长率'], ascending=True, inplace=True)
     df = df.reset_index(drop=True)
-    # df.to_csv(f"D:\Daniel\策略\策略穷举{dt.now().strftime('%Y%m%d')}.csv", index=True, encoding='utf-8', mode='w')
-    df.to_csv(f"/Users/daniel/Documents/策略/策略穷举{dt.now().strftime('%Y%m%d')}.csv", index=True, encoding='utf-8', mode='w')
+    df.to_csv(f"C:\Daniel\策略\策略穷举{dt.now().strftime('%Y%m%d')}.csv", index=True, encoding='utf-8', mode='w')
+    # df.to_csv(f"/Users/daniel/Documents/策略/策略穷举{dt.now().strftime('%Y%m%d')}.csv", index=True, encoding='utf-8', mode='w')
     print(df)
 
 

+ 336 - 0
backtrader/230224_backtrader.py

@@ -0,0 +1,336 @@
+import os
+import traceback
+
+import numpy as np
+from sqlalchemy import create_engine
+import pandas as pd
+import pymysql
+import backtrader as bt
+import backtrader.indicators as btind
+import datetime
+import math
+from datetime import datetime as dt
+import multiprocessing as mp
+from backtrader.feeds import PandasData
+import platform
+
+
+# import multiprocessing
+# import matplotlib
+
+class MyPandasData(PandasData):
+    lines = ('hl', 'dif', 'dea', 'macd', 'rsi_6', 'rsi_12', 'rsi_24',)
+    params = (('hl', 7),
+              ('dif', 8),
+              ('dea', 9),
+              ('macd', 10),
+              ('rsi_6', 11),
+              ('rsi_12', 12),
+              ('rsi_24', 13),
+              )
+    '''
+    lines = ('change_pct', 'net_amount_main', 'net_pct_main', 'net_amount_xl', 'net_pct_xl', 'net_amount_l', 'net_pct_l'
+             , 'net_amount_m', 'net_pct_m', 'net_amount_s', 'net_pct_s',)
+    params = (('change_pct', 7),
+              ('net_amount_main', 8),
+              ('net_pct_main', 9),
+              ('net_amount_xl', 10),
+              ('net_pct_xl', 11),
+              ('net_amount_l', 12),
+              ('net_pct_l', 13),
+              ('net_amount_m', 14),
+              ('net_pct_m', 15),
+              ('net_amount_s', 16),
+              ('net_pct_s', 17),
+              )
+    '''
+
+
+class TestStrategy(bt.Strategy):
+    params = (
+        ("num", 3),
+        ('Volatility', 0),
+        ('rate', 5),  # 注意要有逗号!!
+    )
+
+    def log(self, txt, dt=None):
+        ''' Logging function for this strategy'''
+        dt = dt or self.datas[0].datetime.date(0)
+        # print('%s, %s' % (dt.isoformat(), txt))
+
+    def __init__(self):
+        # self.num = num
+        # self.Volatility = Volatility/100
+        # Keep a reference to the "close" line in the data[0] dataseries
+        self.pos_price = 0
+        self.dataclose = self.datas[0].close
+        self.dataopen = self.datas[0].open
+        self.high = self.datas[0].high
+        self.low = self.datas[0].low
+        self.volume = self.datas[0].volume
+        self.hl = self.datas[0].hl
+        self.dif = self.datas[0].dif
+        self.dea = self.datas[0].dea
+        self.macd = self.datas[0].macd
+        self.rsi_6 = self.datas[0].rsi_6
+        self.rsi_12 = self.datas[0].rsi_12
+        self.rsi_24 = self.datas[0].rsi_24
+        # self.change_pct = self.datas[0].change_pct
+        # self.net_amount_main = self.datas[0].net_amount_main
+        # self.net_pct_main = self.datas[0].net_pct_main
+        # self.net_amount_xl = self.datas[0].net_amount_xl
+        # self.net_pct_xl = self.datas[0].net_pct_xl
+        # self.net_amount_l = self.datas[0].net_amount_l
+        # self.net_pct_l = self.datas[0].net_pct_l
+        self.sma5 = btind.MovingAverageSimple(self.datas[0].close, period=5)
+        self.sma10 = btind.MovingAverageSimple(self.datas[0].close, period=10)
+        self.sma20 = btind.MovingAverageSimple(self.datas[0].close, period=20)
+
+    def notify_order(self, order):
+        """
+        订单状态处理
+
+        Arguments:
+            order {object} -- 订单状态
+        """
+        if order.status in [order.Submitted, order.Accepted]:
+            # 如订单已被处理,则不用做任何事情
+            return
+
+        # 检查订单是否完成
+        if order.status in [order.Completed]:
+            if order.isbuy():
+                self.buyprice = order.executed.price
+                self.buycomm = order.executed.comm
+            self.bar_executed = len(self)
+
+        # 订单因为缺少资金之类的原因被拒绝执行
+        elif order.status in [order.Canceled, order.Margin, order.Rejected]:
+            pass
+            # self.log('Order Canceled/Margin/Rejected')
+
+        # 订单状态处理完成,设为空
+        self.order = None
+
+    def notify_trade(self, trade):
+        """
+        交易成果
+
+        Arguments:
+            trade {object} -- 交易状态
+        """
+        if not trade.isclosed:
+            return
+
+        # 显示交易的毛利率和净利润
+        # self.log('OPERATION PROFIT, GROSS %.2f, NET %.2f' % (trade.pnl, trade.pnlcomm))
+
+    def next(self):
+        # print(self.num,self.Volatility)
+        # Simply log the closing price of the series from the reference
+        # self.sma20[-2] < self.sma20[-1] < self.sma20[0] and self.sma10[-2] < self.sma10[-1] < self.sma10[0]
+        # and (self.sma5[-1] < self.sma10[-1])
+        # and (self.net_pct_l[0] > 10) and (self.net_pct_xl[0] > 3)  \
+        # and (self.net_amount_main[-1] > 0) and (self.net_amount_main[0] > 0)
+
+
+        if len(self) > self.params.num:
+            vola = self.params.Volatility / 100
+            rate = self.params.rate / 100
+            lowest = np.min(self.low.get(size=self.params.num))
+            highest = np.max(self.high.get(size=self.params.num))
+            if (self.hl[-2] == 2 or self.hl[-2] == 1) and self.dataclose[0] > self.sma5[0] > self.sma5[-1] \
+                    and self.sma5[-3]/self.sma5[-2] > self.sma5[-2]/self.sma5[-1] > (1+rate) \
+                    and self.sma5[0] > self.sma5[-1]\
+                    and (((lowest * (1 - vola)) < self.low[-2] < (lowest * (1 + vola))) or (
+                    (lowest * (1 - vola)) < self.low[-1] < (lowest * (1 + vola)))) \
+                    and self.volume[0] >= self.volume[-1] and self.dataclose[0] > self.dataopen[0] \
+                    and self.sma5[0] < self.sma10[0] < self.sma20:
+                self.order = self.buy()
+                self.pos_price = self.low[-1]
+            elif (self.hl[0] == 5 and ((highest * (1 - vola)) < self.high[-2] < (highest * (1 + vola)))) \
+                    or self.dataclose[0] < self.pos_price:
+                self.order = self.close()
+                self.pos_price = 0
+
+
+
+    def stop(self):
+        # pass
+        self.log(u'(MA趋势交易效果) Ending Value %.2f' % (self.broker.getvalue()))
+
+
+def err_call_back(err):
+    print(f'出错啦~ error:{str(err)}')
+    traceback.format_exc(err)
+
+
+def to_df(lt):
+    print('开始存数据')
+    df = pd.DataFrame(list(lt), columns=['周期', '波动率', 'MA5斜率', '盈利个数', '盈利比例', '总盈利', '平均盈利', '最大盈利',
+                               '最小盈利', '总亏损', '平均亏损', '最大亏损', '最小亏损'])
+    df.sort_values(by=['周期', '波动率', 'MA5斜率'], ascending=True, inplace=True)
+    df = df.reset_index(drop=True)
+    if platform.node() == 'DanieldeMBP.lan':
+        df.to_csv(f"/Users/daniel/Documents/策略/策略穷举{dt.now().strftime('%Y%m%d')}.csv", index=True, encoding='utf-8', mode='w')
+    else:
+        df.to_csv(f"C:\Daniel\策略\策略穷举{dt.now().strftime('%Y%m%d')}.csv", index=True, encoding='utf-8', mode='w')
+    print(f'结果:{df}')
+
+
+def backtrader(list_date, table_list, result, result_change, result_change_fall, num, Volatility, rate, err_list):
+    print(f'{num}天波动率为{Volatility}%MA5斜率为{rate}', 'myPID is ', os.getpid())
+    sttime = dt.now()
+    engine = create_engine('mysql+pymysql://root:r6kEwqWU9!v3@localhost:3307/qmt_stocks_tech?charset=utf8')
+    for stock in table_list:
+        # print(stock)
+        stk_df = pd.read_sql_table(stock, engine)
+        stk_df.time = pd.to_datetime(stk_df.time)
+        try:
+            stk_df['HL'] = stk_df['HL'].map({'L': 1,
+                                             'LL': 2,
+                                             'L*': 3,
+                                             'H': 4,
+                                             'HH': 5,
+                                             'H*': 6,
+                                             '-': 7})
+        except BaseException:
+            print(f'{stock}数据不全,不做测试')
+        else:
+            if len(stk_df) > 60:
+                cerebro = bt.Cerebro()
+                cerebro.addstrategy(TestStrategy, num=num, Volatility=Volatility, rate=rate)
+                cerebro.addsizer(bt.sizers.FixedSize, stake=10000)
+                data = MyPandasData(dataname=stk_df,
+                                    fromdate=datetime.datetime(2017, 1, 1),
+                                    todate=datetime.datetime(2022, 10, 30),
+                                    datetime='time',
+                                    open='open_back',
+                                    close='close_back',
+                                    high='high_back',
+                                    low='low_back',
+                                    volume='volume_back',
+                                    hl='HL',
+                                    dif='dif',
+                                    dea='dea',
+                                    macd='macd',
+                                    rsi_6='rsi_6',
+                                    rsi_12='rsi_12',
+                                    rsi_24='rsi_24',
+                                    # change,_pct='change_pct',
+                                    # net_amount_main='net_amount_main',
+                                    # net_pct_main='net_pct_main',
+                                    # net_amount_xl='net_amount_xl',
+                                    # net_pct_xl='net_pct_xl',
+                                    # net_amount_l='net_amount_l',
+                                    # net_pct_l='net_pct_l',
+                                    # net_amount_m='net_amount_m',
+                                    # net_pct_m='net_pct_m',
+                                    # net_amount_s='net_amount_s',
+                                    # net_pct_s='net_pct_s',
+                                    )
+                # print('取值完成')
+                cerebro.adddata(data, name=stock)
+                cerebro.broker.setcash(100000.0)
+                cerebro.broker.setcommission(0.005)
+                cerebro.addanalyzer(bt.analyzers.PyFolio)
+                # 策略执行前的资金
+                # print('启动资金: %.2f' % cerebro.broker.getvalue())
+                try:
+                    # 策略执行
+                    cerebro.run()
+                except IndexError:
+                    err_list.append(stock)
+                else:
+                    if cerebro.broker.getvalue() > 100000.0:
+                        result_change.append((cerebro.broker.getvalue() / 10000 - 1))
+                        result.append(stock)
+                        # print('recode!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!')
+                        # print(result)
+                    else:
+                        result_change_fall.append((1 - cerebro.broker.getvalue() / 10000))
+                        # print('aaaaaaaaaaa')
+                        # print(result_change_fall)
+
+    if len(result) * len(result_change) * len(result_change_fall) != 0:
+        print(f'以{num}内最低值波动{Volatility}为支撑、MA5斜率为{rate}%,结果状态为:')
+        print('正盈利的个股为:', len(result_change), '成功率为:', len(result) / len(table_list))
+        print(
+            f'总盈利:{np.sum(result_change)} 平均盈利:{np.mean(result_change)},最大盈利:{np.max(result_change)}, 最小盈利:{np.min(result_change)}')
+        print(
+            f'总亏损:{np.sum(result_change_fall)},平均亏损:{np.mean(result_change_fall)},最大亏损:{np.min(result_change_fall)} 最小亏损:{np.max(result_change_fall)}')
+
+        list_date.append([num, Volatility, rate, len(result), len(result) / len(table_list), np.nansum(result_change),
+                          np.nanmean(result_change), np.nanmax(result_change), np.min(result_change),
+                          np.nansum(result_change_fall), np.nanmean(result_change_fall),
+                          np.nanmin(result_change_fall), np.nanmax(result_change_fall)])
+        to_df(list_date)
+        endtime = dt.now()
+        print(f'{num}天波动率为{Volatility}%MA5斜率为{rate},myPID is {os.getpid()}.本轮耗时为{endtime - sttime}')
+    else:
+        print('阿欧', len(result), len(result_change), len(result_change_fall), num, Volatility, rate, err_list)
+        list_date.append([num, Volatility, rate, 0, len(result) / len(table_list), len(result),
+                          len(result), len(result), len(result), len(result), len(result), len(result), 0])
+    to_df(list_date)
+    # cerebro.plot()
+
+
+# df = pd.DataFrame(
+#     columns=['周期', '波动率', 'MA5斜率', '盈利个数', '盈利比例', '总盈利', '平均盈利', '最大盈利', '最小盈利', '总亏损',
+#              '平均亏损', '最大亏损', '最小亏损'])
+if __name__ == '__main__':
+    starttime = dt.now()
+    print(starttime)
+    print(type(platform.node()))
+    # engine = create_engine('mysql+pymysql://root:r6kEwqWU9!v3@localhost:3307/hlfx?charset=utf8', poolclass=NullPool)
+
+    # stocks = pd.read_sql_query(
+    #                     'select value from MA5_1d', engine_hlfx)
+
+    fre = '1d'
+    db = pymysql.connect(host='localhost',
+                         user='root',
+                         port=3307,
+                         password='r6kEwqWU9!v3',
+                         database='qmt_stocks_tech')
+    cursor = db.cursor()
+    cursor.execute("show tables like '%%%s%%' " % fre)
+    table_list = [tuple[0] for tuple in cursor.fetchall()]
+    # print(table_list)
+    table_list = table_list[0:10]
+
+    list_date = mp.Manager().list()
+    thread_list = []
+    pool = mp.Pool(processes=mp.cpu_count())
+    for num in range(60, 180, 20):
+        for Volatility in range(5, 8, 1):
+            for rate in range(1, 3, 1):
+                step = math.ceil(len(table_list) / mp.cpu_count())
+                result = []
+                result_change = []
+                result_change_fall = []
+                err_list = []
+                print(f'{num}天波动率为{Volatility}%MA5斜率为{rate}')
+                # for i in range(0, len(table_list), step):
+                stattime = dt.now()
+                # thd = threading.local()
+                # print(i)
+                # p = mp.Process(target=backtrader, args=(df, table_list, result, result_change, result_change_fall,
+                #                                         num, Volatility, rate, err_list))
+                # thread_list.append(p)
+                pool.apply_async(func=backtrader,
+                                 args=(list_date, table_list, result, result_change, result_change_fall,
+                                       num, Volatility, rate, err_list,), error_callback=err_call_back)
+                # p.start()
+                # p.join()
+                # print(thread_list)
+    # for thread in thread_list:
+    #     thread.start()
+    # for thread in thread_list:
+    #     thread.join()
+    pool.close()
+    pool.join()
+
+    edtime = dt.now()
+    print('总耗时:', edtime - starttime)
+    # df.to_csv(r'C:\Users\Daniel\Documents\策略穷举2.csv', index=True)

+ 329 - 0
backtrader/230429_bt.py

@@ -0,0 +1,329 @@
+import os
+import traceback
+import numpy as np
+from sqlalchemy import create_engine
+import pandas as pd
+import pymysql
+import backtrader as bt
+import backtrader.indicators as btind
+import datetime
+import math
+from datetime import datetime as dt
+import multiprocessing as mp
+from backtrader.feeds import PandasData
+import platform
+# import multiprocessing
+# import matplotlib
+
+class MyPandasData(PandasData):
+    lines = ('hl', 'dif', 'dea', 'macd', 'rsi_6', 'rsi_12', 'rsi_24',)
+    params = (('hl', 7),
+              ('dif', 8),
+              ('dea', 9),
+              ('macd', 10),
+              ('rsi_6', 11),
+              ('rsi_12', 12),
+              ('rsi_24', 13),
+              )
+    '''
+    lines = ('change_pct', 'net_amount_main', 'net_pct_main', 'net_amount_xl', 'net_pct_xl', 'net_amount_l', 'net_pct_l'
+             , 'net_amount_m', 'net_pct_m', 'net_amount_s', 'net_pct_s',)
+    params = (('change_pct', 7),
+              ('net_amount_main', 8),
+              ('net_pct_main', 9),
+              ('net_amount_xl', 10),
+              ('net_pct_xl', 11),
+              ('net_amount_l', 12),
+              ('net_pct_l', 13),
+              ('net_amount_m', 14),
+              ('net_pct_m', 15),
+              ('net_amount_s', 16),
+              ('net_pct_s', 17),
+              )
+    '''
+
+
+class TestStrategy(bt.Strategy):
+    params = (
+        ("num", 3),
+        ('Volatility', 0),
+        ('rate', 5),  # 注意要有逗号!!
+    )
+
+    def log(self, txt, dt=None):
+        ''' Logging function for this strategy'''
+        dt = dt or self.datas[0].datetime.date(0)
+        # print('%s, %s' % (dt.isoformat(), txt))
+
+    def __init__(self):
+        # self.num = num
+        # self.Volatility = Volatility/100
+        # Keep a reference to the "close" line in the data[0] dataseries
+        self.pos_price = 0
+        self.dataclose = self.datas[0].close
+        self.dataopen = self.datas[0].open
+        self.high = self.datas[0].high
+        self.low = self.datas[0].low
+        self.volume = self.datas[0].volume
+        self.hl = self.datas[0].hl
+        self.dif = self.datas[0].dif
+        self.dea = self.datas[0].dea
+        self.macd = self.datas[0].macd
+        self.rsi_6 = self.datas[0].rsi_6
+        self.rsi_12 = self.datas[0].rsi_12
+        self.rsi_24 = self.datas[0].rsi_24
+        # self.change_pct = self.datas[0].change_pct
+        # self.net_amount_main = self.datas[0].net_amount_main
+        # self.net_pct_main = self.datas[0].net_pct_main
+        # self.net_amount_xl = self.datas[0].net_amount_xl
+        # self.net_pct_xl = self.datas[0].net_pct_xl
+        # self.net_amount_l = self.datas[0].net_amount_l
+        # self.net_pct_l = self.datas[0].net_pct_l
+        self.sma5 = btind.MovingAverageSimple(self.datas[0].close, period=5)
+        self.sma10 = btind.MovingAverageSimple(self.datas[0].close, period=10)
+        self.sma20 = btind.MovingAverageSimple(self.datas[0].close, period=20)
+        self.sma60 = btind.MovingAverageSimple(self.datas[0].close, period=60)
+
+    def notify_order(self, order):
+        """
+        订单状态处理
+
+        Arguments:
+            order {object} -- 订单状态
+        """
+        if order.status in [order.Submitted, order.Accepted]:
+            # 如订单已被处理,则不用做任何事情
+            return
+
+        # 检查订单是否完成
+        if order.status in [order.Completed]:
+            if order.isbuy():
+                self.buyprice = order.executed.price
+                self.buycomm = order.executed.comm
+            self.bar_executed = len(self)
+
+        # 订单因为缺少资金之类的原因被拒绝执行
+        elif order.status in [order.Canceled, order.Margin, order.Rejected]:
+            pass
+            # self.log('Order Canceled/Margin/Rejected')
+
+        # 订单状态处理完成,设为空
+        self.order = None
+
+    def notify_trade(self, trade):
+        """
+        交易成果
+
+        Arguments:
+            trade {object} -- 交易状态
+        """
+        if not trade.isclosed:
+            return
+
+        # 显示交易的毛利率和净利润
+        # self.log('OPERATION PROFIT, GROSS %.2f, NET %.2f' % (trade.pnl, trade.pnlcomm))
+
+    def next(self):
+        # print(self.num,self.Volatility)
+        # Simply log the closing price of the series from the reference
+        # self.sma20[-2] < self.sma20[-1] < self.sma20[0] and self.sma10[-2] < self.sma10[-1] < self.sma10[0]
+        # and (self.sma5[-1] < self.sma10[-1])
+        # and (self.net_pct_l[0] > 10) and (self.net_pct_xl[0] > 3)  \
+        # and (self.net_amount_main[-1] > 0) and (self.net_amount_main[0] > 0)
+
+        if len(self) > self.params.num:
+            vola = self.params.Volatility / 100
+            rate = self.params.rate / 100
+            lowest = np.min(self.low.get(size=self.params.num))
+            highest = np.max(self.high.get(size=self.params.num))
+            if (self.hl[-2] == 2 or self.hl[-2] == 1) and self.dataclose[0] > self.sma5[0] > self.sma5[-1] \
+                    and self.sma5[-3]/self.sma5[-2] > self.sma5[-2]/self.sma5[-1] > (1+rate) \
+                    and self.sma5[0] > self.sma5[-1] > (1+rate)\
+                    and (((lowest * (1 - vola)) < self.low[-2] < (lowest * (1 + vola))) or (
+                    (lowest * (1 - vola)) < self.low[-1] < (lowest * (1 + vola)))) \
+                    and self.volume[0] >= self.volume[-1] and self.dataclose[0] > self.dataopen[0] \
+                    and self.sma5[0] < self.sma10[0] and self.sma10[0] > self.sma20[0]:
+                while True:
+                    m = -3
+                    if (self.hl[m] == 2 or self.hl[m] == 1) and self.macd[m] < self.macd[-1] and self.dataclose[m] > self.dataclose[-1]:
+                        self.order = self.buy()
+                        self.pos_price = self.low[-1]
+                        break
+                    m -= 1
+                    if m < (-len(self)):
+                        break
+            elif ((self.hl[0] == 5 or self.dataclose[0] < self.sma5[0]) and ((highest * (1 - vola)) < self.high[-2] < (highest * (1 + vola)))) \
+                    or self.dataclose[0] < self.pos_price:
+                self.order = self.close()
+                self.pos_price = 0
+
+
+
+    def stop(self):
+        # pass
+        self.log(u'(MA趋势交易效果) Ending Value %.2f' % (self.broker.getvalue()))
+
+
+def err_call_back(err):
+    print(f'出错啦~ error:{str(err)}')
+    traceback.format_exc(err)
+
+
+def to_df(lt):
+    print('开始存数据')
+    df = pd.DataFrame(list(lt), columns=['周期', '波动率', 'MA5斜率', '盈利个数', '盈利比例', '总盈利', '平均盈利', '最大盈利',
+                               '最小盈利', '总亏损', '平均亏损', '最大亏损', '最小亏损'])
+    df.sort_values(by=['周期', '波动率', 'MA5斜率'], ascending=True, inplace=True)
+    df = df.reset_index(drop=True)
+    if platform.node() == 'DanieldeMBP.lan':
+        df.to_csv(f"/Users/daniel/Documents/策略/策略穷举{dt.now().strftime('%Y%m%d')}.csv", index=True, encoding='utf-8', mode='w')
+    else:
+        df.to_csv(f"C:\Daniel\策略\策略穷举{dt.now().strftime('%Y%m%d')}.csv", index=True, encoding='utf-8', mode='w')
+    print(f'结果:{df}')
+
+
+def backtrader(list_date, table_list, result, result_change, result_change_fall, num, Volatility, rate, err_list):
+    print(f'{num}天波动率为{Volatility}%MA5斜率为{rate}', 'myPID is ', os.getpid())
+    sttime = dt.now()
+    engine = create_engine('mysql+pymysql://root:r6kEwqWU9!v3@localhost:3307/qmt_stocks_tech?charset=utf8')
+    for stock in table_list:
+        # print(stock)
+        stk_df = pd.read_sql_table(stock, engine)
+        stk_df.time = pd.to_datetime(stk_df.time)
+        try:
+            stk_df['HL'] = stk_df['HL'].map({'L': 1,
+                                             'LL': 2,
+                                             'L*': 3,
+                                             'H': 4,
+                                             'HH': 5,
+                                             'H*': 6,
+                                             '-': 7})
+        except BaseException:
+            print(f'{stock}数据不全,不做测试')
+        else:
+            if len(stk_df) > 60:
+                cerebro = bt.Cerebro()
+                cerebro.addstrategy(TestStrategy, num=num, Volatility=Volatility, rate=rate)
+                cerebro.addsizer(bt.sizers.FixedSize, stake=10000)
+                data = MyPandasData(dataname=stk_df,
+                                    fromdate=datetime.datetime(2017, 1, 1),
+                                    todate=datetime.datetime(2022, 10, 30),
+                                    datetime='time',
+                                    open='open_back',
+                                    close='close_back',
+                                    high='high_back',
+                                    low='low_back',
+                                    volume='volume_back',
+                                    hl='HL',
+                                    dif='dif',
+                                    dea='dea',
+                                    macd='macd',
+                                    rsi_6='rsi_6',
+                                    rsi_12='rsi_12',
+                                    rsi_24='rsi_24',
+                                    )
+                # print('取值完成')
+                cerebro.adddata(data, name=stock)
+                cerebro.broker.setcash(100000.0)
+                cerebro.broker.setcommission(0.005)
+                cerebro.addanalyzer(bt.analyzers.PyFolio)
+                # 策略执行前的资金
+                # print('启动资金: %.2f' % cerebro.broker.getvalue())
+                try:
+                    # 策略执行
+                    cerebro.run()
+                except IndexError:
+                    err_list.append(stock)
+                else:
+                    if cerebro.broker.getvalue() > 100000.0:
+                        result_change.append((cerebro.broker.getvalue() / 10000 - 1))
+                        result.append(stock)
+                        # print('recode!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!')
+                        # print(result)
+                    else:
+                        result_change_fall.append((1 - cerebro.broker.getvalue() / 10000))
+                        # print('aaaaaaaaaaa')
+                        # print(result_change_fall)
+
+    if len(result) * len(result_change) * len(result_change_fall) != 0:
+        print(f'以{num}内最低值波动{Volatility}为支撑、MA5斜率为{rate}%,结果状态为:')
+        print('正盈利的个股为:', len(result_change), '成功率为:', len(result) / len(table_list))
+        print(
+            f'总盈利:{np.sum(result_change)} 平均盈利:{np.mean(result_change)},最大盈利:{np.max(result_change)}, 最小盈利:{np.min(result_change)}')
+        print(
+            f'总亏损:{np.sum(result_change_fall)},平均亏损:{np.mean(result_change_fall)},最大亏损:{np.min(result_change_fall)} 最小亏损:{np.max(result_change_fall)}')
+
+        list_date.append([num, Volatility, rate, len(result), len(result) / len(table_list), np.nansum(result_change),
+                          np.nanmean(result_change), np.nanmax(result_change), np.min(result_change),
+                          np.nansum(result_change_fall), np.nanmean(result_change_fall),
+                          np.nanmin(result_change_fall), np.nanmax(result_change_fall)])
+        to_df(list_date)
+        endtime = dt.now()
+        print(f'{num}天波动率为{Volatility}%MA5斜率为{rate},myPID is {os.getpid()}.本轮耗时为{endtime - sttime}')
+    else:
+        print('阿欧', len(result), len(result_change), len(result_change_fall), num, Volatility, rate, err_list)
+        list_date.append([num, Volatility, rate, 0, len(result) / len(table_list), len(result),
+                          len(result), len(result), len(result), len(result), len(result), len(result), 0])
+    to_df(list_date)
+    # cerebro.plot()
+
+
+# df = pd.DataFrame(
+#     columns=['周期', '波动率', 'MA5斜率', '盈利个数', '盈利比例', '总盈利', '平均盈利', '最大盈利', '最小盈利', '总亏损',
+#              '平均亏损', '最大亏损', '最小亏损'])
+if __name__ == '__main__':
+    starttime = dt.now()
+    print(starttime)
+    # print(type(platform.node()))
+    # engine = create_engine('mysql+pymysql://root:r6kEwqWU9!v3@localhost:3307/hlfx?charset=utf8', poolclass=NullPool)
+
+    # stocks = pd.read_sql_query(
+    #                     'select value from MA5_1d', engine_hlfx)
+
+    fre = '1d'
+    db = pymysql.connect(host='localhost',
+                         user='root',
+                         port=3307,
+                         password='r6kEwqWU9!v3',
+                         database='qmt_stocks_tech')
+    cursor = db.cursor()
+    cursor.execute("show tables like '%%%s%%' " % fre)
+    table_list = [tuple[0] for tuple in cursor.fetchall()]
+    # print(table_list)
+    # table_list = table_list[0:10]
+
+    list_date = mp.Manager().list()
+    thread_list = []
+    pool = mp.Pool(processes=mp.cpu_count())
+    for num in range(60, 180, 20):
+        for Volatility in range(5, 10, 1):
+            for rate in range(1, 4, 1):
+                step = math.ceil(len(table_list) / mp.cpu_count())
+                result = []
+                result_change = []
+                result_change_fall = []
+                err_list = []
+                print(f'{num}天波动率为{Volatility}%MA5斜率为{rate}')
+                # for i in range(0, len(table_list), step):
+                stattime = dt.now()
+                # thd = threading.local()
+                # print(i)
+                # p = mp.Process(target=backtrader, args=(df, table_list, result, result_change, result_change_fall,
+                #                                         num, Volatility, rate, err_list))
+                # thread_list.append(p)
+                pool.apply_async(func=backtrader,
+                                 args=(list_date, table_list, result, result_change, result_change_fall,
+                                       num, Volatility, rate, err_list,), error_callback=err_call_back)
+                # p.start()
+                # p.join()
+                # print(thread_list)
+    # for thread in thread_list:
+    #     thread.start()
+    # for thread in thread_list:
+    #     thread.join()
+    pool.close()
+    pool.join()
+
+    edtime = dt.now()
+    print('总耗时:', edtime - starttime)
+    # df.to_csv(r'C:\Users\Daniel\Documents\策略穷举2.csv', index=True)

+ 343 - 0
backtrader/230503_bt.py

@@ -0,0 +1,343 @@
+import os
+import traceback
+import numpy as np
+from sqlalchemy import create_engine
+import pandas as pd
+import pymysql
+import backtrader as bt
+import backtrader.indicators as btind
+import datetime
+import math
+from datetime import datetime as dt
+import multiprocessing as mp
+from backtrader.feeds import PandasData
+import platform
+import psutil
+# import multiprocessing
+# import matplotlib
+
+
+class MyPandasData(PandasData):
+    lines = ('hl', 'dif', 'dea', 'macd', 'rsi_6', 'rsi_12', 'rsi_24',)
+    params = (('hl', 7),
+              ('dif', 8),
+              ('dea', 9),
+              ('macd', 10),
+              ('rsi_6', 11),
+              ('rsi_12', 12),
+              ('rsi_24', 13),
+              )
+    '''
+    lines = ('change_pct', 'net_amount_main', 'net_pct_main', 'net_amount_xl', 'net_pct_xl', 'net_amount_l', 'net_pct_l'
+             , 'net_amount_m', 'net_pct_m', 'net_amount_s', 'net_pct_s',)
+    params = (('change_pct', 7),
+              ('net_amount_main', 8),
+              ('net_pct_main', 9),
+              ('net_amount_xl', 10),
+              ('net_pct_xl', 11),
+              ('net_amount_l', 12),
+              ('net_pct_l', 13),
+              ('net_amount_m', 14),
+              ('net_pct_m', 15),
+              ('net_amount_s', 16),
+              ('net_pct_s', 17),
+              )
+    '''
+
+
+class TestStrategy(bt.Strategy):
+    params = (
+        ("num", 3),
+        ('Volatility', 0),
+        ('rate', 3),  # 注意要有逗号!!
+    )
+
+    def log(self, txt, dt=None):
+        ''' Logging function for this strategy'''
+        dt = dt or self.datas[0].datetime.date(0)
+        # print('%s, %s' % (dt.isoformat(), txt))
+
+    def __init__(self):
+        # self.num = num
+        # self.Volatility = Volatility/100
+        # Keep a reference to the "close" line in the data[0] dataseries
+        self.pos_price = 0
+        self.dataclose = self.datas[0].close
+        self.dataopen = self.datas[0].open
+        self.high = self.datas[0].high
+        self.low = self.datas[0].low
+        self.volume = self.datas[0].volume
+        self.hl = self.datas[0].hl
+        self.dif = self.datas[0].dif
+        self.dea = self.datas[0].dea
+        self.macd = self.datas[0].macd
+        self.rsi_6 = self.datas[0].rsi_6
+        self.rsi_12 = self.datas[0].rsi_12
+        self.rsi_24 = self.datas[0].rsi_24
+        # self.change_pct = self.datas[0].change_pct
+        # self.net_amount_main = self.datas[0].net_amount_main
+        # self.net_pct_main = self.datas[0].net_pct_main
+        # self.net_amount_xl = self.datas[0].net_amount_xl
+        # self.net_pct_xl = self.datas[0].net_pct_xl
+        # self.net_amount_l = self.datas[0].net_amount_l
+        # self.net_pct_l = self.datas[0].net_pct_l
+        self.sma5 = btind.MovingAverageSimple(self.datas[0].close, period=5)
+        self.sma10 = btind.MovingAverageSimple(self.datas[0].close, period=10)
+        self.sma20 = btind.MovingAverageSimple(self.datas[0].close, period=20)
+        self.sma60 = btind.MovingAverageSimple(self.datas[0].close, period=60)
+
+    def notify_order(self, order):
+        """
+        订单状态处理
+
+        Arguments:
+            order {object} -- 订单状态
+        """
+        if order.status in [order.Submitted, order.Accepted]:
+            # 如订单已被处理,则不用做任何事情
+            return
+
+        # 检查订单是否完成
+        if order.status in [order.Completed]:
+            if order.isbuy():
+                self.buyprice = order.executed.price
+                self.buycomm = order.executed.comm
+            self.bar_executed = len(self)
+
+        # 订单因为缺少资金之类的原因被拒绝执行
+        elif order.status in [order.Canceled, order.Margin, order.Rejected]:
+            pass
+            # self.log('Order Canceled/Margin/Rejected')
+
+        # 订单状态处理完成,设为空
+        self.order = None
+
+    def notify_trade(self, trade):
+        """
+        交易成果
+
+        Arguments:
+            trade {object} -- 交易状态
+        """
+        if not trade.isclosed:
+            return
+
+        # 显示交易的毛利率和净利润
+        # self.log('OPERATION PROFIT, GROSS %.2f, NET %.2f' % (trade.pnl, trade.pnlcomm))
+
+    def next(self):
+        # print(self.num,self.Volatility)
+        # Simply log the closing price of the series from the reference
+        # self.sma20[-2] < self.sma20[-1] < self.sma20[0] and self.sma10[-2] < self.sma10[-1] < self.sma10[0]
+        # and (self.sma5[-1] < self.sma10[-1])
+        # and (self.net_pct_l[0] > 10) and (self.net_pct_xl[0] > 3)  \
+        # and (self.net_amount_main[-1] > 0) and (self.net_amount_main[0] > 0)
+
+
+
+        if len(self) > self.params.num:
+            vola = self.params.Volatility / 100
+            rate = self.params.rate / 100
+            lowest = np.min(self.low.get(size=self.params.num))
+            highest = np.max(self.high.get(size=self.params.num))
+
+            #
+            # > self.sma5[-1]
+            # and (((lowest * (1 - vola)) < self.low[-2] < (lowest * (1 + vola))) or (
+            #         (lowest * (1 - vola)) < self.low[-1] < (lowest * (1 + vola)))) \
+            '''
+                        if (self.hl[-1] == 2 or self.hl[-1] == 1) and self.dataclose[0] > self.sma5[0]  \
+                    and self.dataclose[-1] > self.dataopen[-1] \
+                    and (self.sma10[-2] - self.sma5[-2]) < (self.sma10[-1] - self.sma5[-1]) \
+                    and self.low[-2] < self.sma5[-2] * (1 - rate) \
+                    and self.sma5[-1] < self.sma10[-1] < self.sma20[-1] < self.sma20[-2] < self.sma20[-3] < self.sma20[-4]:
+            '''
+            if (self.hl[0] == 1 or self.hl[0] == 2 or self.hl[0] == 3) \
+                    and (1-vola)*lowest < self.low[-1] < (1+vola)*lowest\
+                    and (self.sma10[-2] - self.sma5[-2]) < (self.sma10[-1] - self.sma5[-1]):
+            # if self.dataclose[0] > self.sma5[0] > self.sma10[0] and self.sma5[-1] < self.sma10[-1]:
+                self.order = self.buy()
+                self.pos_price = self.low[-1]
+
+            elif ((self.hl[0] == 4 or self.hl[0] == 5 or self.hl[0] == 6) and self.dataclose < self.sma5[0]) or self.dataclose[0] < self.pos_price:
+                    self.order = self.close()
+                    self.pos_price = 0
+
+
+            # elif (self.hl[0] == 5 or self.dataclose[0] < self.sma5[0]):
+            '''
+                        elif self.dataclose[0] < self.sma10[0] or self.sma5[0] < self.sma5[-1] or self.dataclose[0] < self.pos_price\
+                    or len(self) == (self.buflen()-1):
+            '''
+
+    def stop(self):
+        # pass
+        self.log(u'(MA趋势交易效果) Ending Value %.2f' % (self.broker.getvalue()))
+
+
+def err_call_back(err):
+    print(f'出错啦~ error:{str(err)}')
+    traceback.format_exc(err)
+
+
+def to_df(lt):
+    print('开始存数据')
+    df = pd.DataFrame(list(lt), columns=['周期', '波动率', 'MA5斜率', '盈利个数', '盈利比例', '总盈利', '平均盈利', '最大盈利',
+                               '最小盈利', '总亏损', '平均亏损', '最大亏损', '最小亏损', '盈亏对比'])
+    df.sort_values(by=['周期', '波动率', 'MA5斜率'], ascending=True, inplace=True)
+    df = df.reset_index(drop=True)
+    if platform.node() == 'DanieldeMBP.lan':
+        df.to_csv(f"/Users/daniel/Documents/策略/策略穷举{dt.now().strftime('%Y%m%d%H%m%S')}.csv", index=True, encoding='utf_8_sig', mode='w')
+    else:
+        df.to_csv(f"C:\Daniel\策略\策略穷举周期低点{dt.now().strftime('%Y%m%d%H%m%S' )}.csv", index=True, encoding='utf_8_sig', mode='w')
+    print(f'结果:{df}')
+
+
+def backtrader(list_date, table_list, result, result_change, result_change_fall, num, Volatility, rate, err_list):
+    print(f'{num}天波动率为{Volatility}%MA5斜率为{rate}', 'myPID is ', os.getpid())
+    sttime = dt.now()
+    engine = create_engine('mysql+pymysql://root:r6kEwqWU9!v3@localhost:3307/qmt_stocks_tech?charset=utf8')
+    for stock in table_list:
+        # print(stock)
+        stk_df = pd.read_sql_table(stock, engine)
+        stk_df.time = pd.to_datetime(stk_df.time)
+        stk_df = stk_df[stk_df['HL'] != '-']
+        try:
+            stk_df['HL'] = stk_df['HL'].map({'L': 1,
+                                             'LL': 2,
+                                             'L*': 3,
+                                             'H': 4,
+                                             'HH': 5,
+                                             'H*': 6,
+                                             '-': 7})
+        except BaseException:
+            print(f'{stock}数据不全,不做测试')
+        else:
+            if len(stk_df) > 60:
+                cerebro = bt.Cerebro()
+                cerebro.addstrategy(TestStrategy, num=num, Volatility=Volatility, rate=rate)
+                cerebro.addsizer(bt.sizers.FixedSize, stake=10000)
+                data = MyPandasData(dataname=stk_df,
+                                    fromdate=datetime.datetime(2017, 1, 1),
+                                    todate=datetime.datetime(2022, 10, 30),
+                                    datetime='time',
+                                    open='open_back',
+                                    close='close_back',
+                                    high='high_back',
+                                    low='low_back',
+                                    volume='volume_back',
+                                    hl='HL',
+                                    dif='dif',
+                                    dea='dea',
+                                    macd='macd',
+                                    rsi_6='rsi_6',
+                                    rsi_12='rsi_12',
+                                    rsi_24='rsi_24',
+                                    )
+                # print('取值完成')
+                cerebro.adddata(data, name=stock)
+                cerebro.broker.setcash(100000.0)
+                cerebro.broker.setcommission(0.005)
+                cerebro.addanalyzer(bt.analyzers.PyFolio)
+                # 策略执行前的资金
+                # print('启动资金: %.2f' % cerebro.broker.getvalue())
+                try:
+                    # 策略执行
+                    cerebro.run()
+                except IndexError as e:
+                    err_list.append(stock)
+                    # print(e)
+                else:
+                    if cerebro.broker.getvalue() > 100000.0:
+                        result_change.append(cerebro.broker.getvalue()-100000)
+                        result.append(stock)
+                        # print('recode!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!')
+                        # print(result)
+                    else:
+                        result_change_fall.append(cerebro.broker.getvalue()-100000)
+                        # if (cerebro.broker.getvalue()-100000) == np.min(result_change_fall):
+                        #     print(stock, (cerebro.broker.getvalue()-100000))
+                        # print('aaaaaaaaaaa')
+                        # print(result_change_fall)
+    print(f'计算总数={len(result)+len(result_change_fall)}')
+    if len(result) * len(result_change) * len(result_change_fall) != 0:
+        print(f'以{num}内最低值波动{Volatility}为支撑、MA5斜率为{rate}%,结果状态为:')
+        print('正盈利的个股为:', len(result), '成功率为:', len(result) / len(table_list))
+        print(
+            f'总盈利:{np.sum(result_change)} 平均盈利:{np.mean(result_change)/len(result)},最大盈利:{np.max(result_change)}, 最小盈利:{np.min(result_change)}')
+        print(
+            f'总亏损:{np.sum(result_change_fall)},平均亏损:{np.mean(result_change_fall)/len(result_change_fall)},最大亏损:{np.min(result_change_fall)} 最小亏损:{np.max(result_change_fall)}')
+
+        # '周期', '波动率', 'MA5斜率', '盈利个数', '盈利比例', '总盈利', '平均盈利', '最大盈利', '最小盈利', '总亏损', '平均亏损', '最大亏损', '最小亏损', '盈亏对比']
+        list_date.append([num, Volatility, rate, len(result), len(result) / len(table_list), np.nansum(result_change),
+                          np.nanmean(result_change), np.nanmax(result_change), np.min(result_change),
+                          np.nansum(result_change_fall), np.nanmean(result_change_fall),
+                          np.nanmin(result_change_fall), np.nanmax(result_change_fall), len(result_change)/len(result_change_fall)])
+        to_df(list_date)
+        endtime = dt.now()
+        print(f'{num}天波动率为{Volatility}%MA5斜率为{rate},myPID is {os.getpid()}.本轮耗时为{endtime - sttime}')
+    else:
+        print('阿欧', len(result), len(result_change), len(result_change_fall), num, Volatility, rate, err_list)
+        list_date.append([num, Volatility, rate, 0, len(result) / len(table_list), len(result),
+                          len(result), len(result), len(result), len(result), len(result), len(result), 0])
+    to_df(list_date)
+    # cerebro.plot()
+
+
+# df = pd.DataFrame(
+#     columns=['周期', '波动率', 'MA5斜率', '盈利个数', '盈利比例', '总盈利', '平均盈利', '最大盈利', '最小盈利', '总亏损',
+#              '平均亏损', '最大亏损', '最小亏损'])
+if __name__ == '__main__':
+    starttime = dt.now()
+    print(starttime)
+    pus = psutil.Process()
+    pus.cpu_affinity([23, 16, 17, 18, 19, 20, 21, 22])
+    # pus.cpu_affinity([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15])
+    # print(type(platform.node()))
+    # engine = create_engine('mysql+pymysql://root:r6kEwqWU9!v3@localhost:3307/hlfx?charset=utf8', poolclass=NullPool)
+
+    # stocks = pd.read_sql_query(
+    #                     'select value from MA5_1d', engine_hlfx)
+
+    fre = '1d'
+    db = pymysql.connect(host='localhost',
+                         user='root',
+                         port=3307,
+                         password='r6kEwqWU9!v3',
+                         database='qmt_stocks_tech')
+    cursor = db.cursor()
+    cursor.execute("show tables like '%%%s%%' " % fre)
+    table_list = [tuple[0] for tuple in cursor.fetchall()]
+    # print(table_list)
+    # table_list = table_list[0:500]
+    print(f'计算个股数为:{len(table_list)}')
+
+    list_date = mp.Manager().list()
+    thread_list = []
+    # pool = mp.Pool(processes=mp.cpu_count())
+    pool = mp.Pool(processes=8)
+    for num in range(20, 200, 20):
+        for Volatility in range(3, 11, 1):
+            for rate in range(1, 2, 1):
+                # step = math.ceil(len(table_list) / mp.cpu_count())
+                result = []
+                result_change = []
+                result_change_fall = []
+                err_list = []
+                print(f'{num}天波动率为{Volatility}%MA5斜率为{rate}')
+                # for i in range(0, len(table_list), step):
+                stattime = dt.now()
+                # thd = threading.local()
+                # print(i)
+                # p = mp.Process(target=backtrader, args=(df, table_list, result, result_change, result_change_fall,
+                #                                         num, Volatility, rate, err_list))
+                # thread_list.append(p)
+                pool.apply_async(func=backtrader,
+                                 args=(list_date, table_list, result, result_change, result_change_fall,
+                                       num, Volatility, rate, err_list,), error_callback=err_call_back)
+    pool.close()
+    pool.join()
+
+    edtime = dt.now()
+    print('总耗时:', edtime - starttime)
+    # df.to_csv(r'C:\Users\Daniel\Documents\策略穷举2.csv', index=True)

+ 346 - 0
backtrader/230505_bt.py

@@ -0,0 +1,346 @@
+import os
+import traceback
+import numpy as np
+from sqlalchemy import create_engine
+import pandas as pd
+import pymysql
+import backtrader as bt
+import backtrader.indicators as btind
+import datetime
+import math
+from datetime import datetime as dt
+import multiprocessing as mp
+from backtrader.feeds import PandasData
+import platform
+import psutil
+
+
+# import multiprocessing
+# import matplotlib
+
+
+class MyPandasData(PandasData):
+    lines = ('hl', 'dif', 'dea', 'macd', 'rsi_6', 'rsi_12', 'rsi_24',)
+    params = (('hl', 7),
+              ('dif', 8),
+              ('dea', 9),
+              ('macd', 10),
+              ('rsi_6', 11),
+              ('rsi_12', 12),
+              ('rsi_24', 13),
+              )
+    '''
+    lines = ('change_pct', 'net_amount_main', 'net_pct_main', 'net_amount_xl', 'net_pct_xl', 'net_amount_l', 'net_pct_l'
+             , 'net_amount_m', 'net_pct_m', 'net_amount_s', 'net_pct_s',)
+    params = (('change_pct', 7),
+              ('net_amount_main', 8),
+              ('net_pct_main', 9),
+              ('net_amount_xl', 10),
+              ('net_pct_xl', 11),
+              ('net_amount_l', 12),
+              ('net_pct_l', 13),
+              ('net_amount_m', 14),
+              ('net_pct_m', 15),
+              ('net_amount_s', 16),
+              ('net_pct_s', 17),
+              )
+    '''
+
+
+class TestStrategy(bt.Strategy):
+    params = (
+        ("num", 3),
+        ('Volatility', 0),
+        ('rate', 3),  # 注意要有逗号!!
+    )
+
+    def log(self, txt, dt=None):
+        ''' Logging function for this strategy'''
+        dt = dt or self.datas[0].datetime.date(0)
+        # print('%s, %s' % (dt.isoformat(), txt))
+
+    def __init__(self):
+        # self.num = num
+        # self.Volatility = Volatility/100
+        # Keep a reference to the "close" line in the data[0] dataseries
+        self.pos_price = 0
+        self.dataclose = self.datas[0].close
+        self.dataopen = self.datas[0].open
+        self.high = self.datas[0].high
+        self.low = self.datas[0].low
+        self.volume = self.datas[0].volume
+        self.hl = self.datas[0].hl
+        self.dif = self.datas[0].dif
+        self.dea = self.datas[0].dea
+        self.macd = self.datas[0].macd
+        self.rsi_6 = self.datas[0].rsi_6
+        self.rsi_12 = self.datas[0].rsi_12
+        self.rsi_24 = self.datas[0].rsi_24
+        # self.change_pct = self.datas[0].change_pct
+        # self.net_amount_main = self.datas[0].net_amount_main
+        # self.net_pct_main = self.datas[0].net_pct_main
+        # self.net_amount_xl = self.datas[0].net_amount_xl
+        # self.net_pct_xl = self.datas[0].net_pct_xl
+        # self.net_amount_l = self.datas[0].net_amount_l
+        # self.net_pct_l = self.datas[0].net_pct_l
+        self.sma5 = btind.MovingAverageSimple(self.datas[0].close, period=5)
+        self.sma10 = btind.MovingAverageSimple(self.datas[0].close, period=10)
+        self.sma20 = btind.MovingAverageSimple(self.datas[0].close, period=20)
+        self.sma60 = btind.MovingAverageSimple(self.datas[0].close, period=60)
+
+    def notify_order(self, order):
+        """
+        订单状态处理
+
+        Arguments:
+            order {object} -- 订单状态
+        """
+        if order.status in [order.Submitted, order.Accepted]:
+            # 如订单已被处理,则不用做任何事情
+            return
+
+        # 检查订单是否完成
+        if order.status in [order.Completed]:
+            if order.isbuy():
+                self.buyprice = order.executed.price
+                self.buycomm = order.executed.comm
+            self.bar_executed = len(self)
+
+        # 订单因为缺少资金之类的原因被拒绝执行
+        elif order.status in [order.Canceled, order.Margin, order.Rejected]:
+            pass
+            # self.log('Order Canceled/Margin/Rejected')
+
+        # 订单状态处理完成,设为空
+        self.order = None
+
+    def notify_trade(self, trade):
+        """
+        交易成果
+
+        Arguments:
+            trade {object} -- 交易状态
+        """
+        if not trade.isclosed:
+            return
+
+        # 显示交易的毛利率和净利润
+        # self.log('OPERATION PROFIT, GROSS %.2f, NET %.2f' % (trade.pnl, trade.pnlcomm))
+
+    def next(self):
+        # print(self.num,self.Volatility)
+        # Simply log the closing price of the series from the reference
+        # self.sma20[-2] < self.sma20[-1] < self.sma20[0] and self.sma10[-2] < self.sma10[-1] < self.sma10[0]
+        # and (self.sma5[-1] < self.sma10[-1])
+        # and (self.net_pct_l[0] > 10) and (self.net_pct_xl[0] > 3)  \
+        # and (self.net_amount_main[-1] > 0) and (self.net_amount_main[0] > 0)
+
+        if len(self) > self.params.num:
+            vola = self.params.Volatility / 100
+            rate = self.params.rate / 100
+            lowest = np.min(self.low.get(size=self.params.num))
+            highest = np.max(self.high.get(size=self.params.num))
+
+            # > self.sma5[-1]
+            # and (((lowest * (1 - vola)) < self.low[-2] < (lowest * (1 + vola))) or (
+            #         (lowest * (1 - vola)) < self.low[-1] < (lowest * (1 + vola)))) \
+            if self.hl[-1] == 2 or self.hl[-1] == 1:
+                m = -2
+                self.order = self.buy()
+                self.pos_price = self.low[-1]
+
+                while True:
+                    if (self.hl[m] == 2 or self.hl[m] == 1) and self.macd[m] > self.macd[-1] \
+                            and self.dataclose[0] > self.sma5[0] \
+                            and self.dataclose[-1] > self.dataopen[-1] \
+                            and (self.sma10[-2] - self.sma5[-2]) < (self.sma10[-1] - self.sma5[-1]) \
+                            and self.low[-2] < self.sma5[-2] * (1 - rate) \
+                            and self.sma5[-1] < self.sma10[-1] < self.sma20[-1] < self.sma20[-2] < self.sma20[-3]\
+                            and lowest*(1-vola) < self.low[-1] < lowest * (1 + vola):
+                        self.order = self.buy()
+                        self.pos_price = self.low[-1]
+                        break
+                    m -= 1
+                    if m + len(self) == 2:
+                        break
+
+            # elif (self.hl[0] == 5 or self.dataclose[0] < self.sma5[0]):
+            elif self.dataclose[0] < self.sma5[0] or self.sma5[0] < self.sma5[-1] \
+                    or self.dataclose[0] < self.pos_price or self.high[0] > self.sma5[0] * (1 + vola):
+                self.order = self.close()
+                self.pos_price = 0
+
+    def stop(self):
+        # pass
+        self.log(u'(MA趋势交易效果) Ending Value %.2f' % (self.broker.getvalue()))
+
+
+def err_call_back(err):
+    print(f'出错啦~ error:{str(err)}')
+    traceback.format_exc(err)
+
+
+def to_df(lt):
+    print('开始存数据')
+    df = pd.DataFrame(list(lt),
+                      columns=['周期', '波动率', 'MA5斜率', '盈利个数', '盈利比例', '总盈利', '平均盈利', '最大盈利',
+                               '最小盈利', '总亏损', '平均亏损', '最大亏损', '最小亏损', '盈亏对比'])
+    df.sort_values(by=['周期', '波动率', 'MA5斜率'], ascending=True, inplace=True)
+    df = df.reset_index(drop=True)
+    if platform.node() == 'DanieldeMBP.lan':
+        df.to_csv(f"/Users/daniel/Documents/策略/策略穷举{dt.now().strftime('%Y%m%d%H%m%S')}.csv", index=True,
+                  encoding='utf_8_sig', mode='w')
+    else:
+        df.to_csv(f"C:\Daniel\策略\策略穷举_底分且均线扩散_rate低点{dt.now().strftime('%Y%m%d%H%m%S')}.csv", index=True, encoding='utf_8_sig',
+                  mode='w')
+    print(f'结果:{df}')
+
+
+def backtrader(list_date, table_list, result, result_change, result_change_fall, num, Volatility, rate, err_list):
+    print(f'{num}天波动率为{Volatility}%MA5斜率为{rate}', 'myPID is ', os.getpid())
+    sttime = dt.now()
+    engine = create_engine('mysql+pymysql://root:r6kEwqWU9!v3@localhost:3307/qmt_stocks_tech?charset=utf8',pool_size=4000)
+    for stock in table_list:
+        # print(stock)
+        stk_df = pd.read_sql_table(stock, engine.connect())
+        stk_df.time = pd.to_datetime(stk_df.time)
+        # stk_df = stk_df[stk_df['HL'] != '-']
+        try:
+            stk_df['HL'] = stk_df['HL'].map({'L': 1,
+                                             'LL': 2,
+                                             'L*': 3,
+                                             'H': 4,
+                                             'HH': 5,
+                                             'H*': 6,
+                                             '-': 7})
+        except BaseException:
+            print(f'{stock}数据不全,不做测试')
+        else:
+            if len(stk_df) > 60:
+                cerebro = bt.Cerebro()
+                cerebro.addstrategy(TestStrategy, num=num, Volatility=Volatility, rate=rate)
+                cerebro.addsizer(bt.sizers.FixedSize, stake=10000)
+                data = MyPandasData(dataname=stk_df,
+                                    fromdate=datetime.datetime(2017, 1, 1),
+                                    todate=datetime.datetime(2022, 10, 30),
+                                    datetime='time',
+                                    open='open_back',
+                                    close='close_back',
+                                    high='high_back',
+                                    low='low_back',
+                                    volume='volume_back',
+                                    hl='HL',
+                                    dif='dif',
+                                    dea='dea',
+                                    macd='macd',
+                                    rsi_6='rsi_6',
+                                    rsi_12='rsi_12',
+                                    rsi_24='rsi_24',
+                                    )
+                # print('取值完成')
+                cerebro.adddata(data, name=stock)
+                cerebro.broker.setcash(100000.0)
+                cerebro.broker.setcommission(0.005)
+                cerebro.addanalyzer(bt.analyzers.PyFolio)
+                # 策略执行前的资金
+                # print('启动资金: %.2f' % cerebro.broker.getvalue())
+                try:
+                    # 策略执行
+                    cerebro.run()
+                except IndexError as e:
+                    err_list.append(stock)
+                    # print(f'{num}天波动率为{Volatility}%MA5斜率为{rate}的{stock}错误')
+                    # print(e)
+                else:
+                    if cerebro.broker.getvalue() > 100000.0:
+                        result_change.append(cerebro.broker.getvalue() - 100000)
+                        result.append(stock)
+                        # print('recode!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!')
+                        # print(result)
+                    else:
+                        result_change_fall.append(cerebro.broker.getvalue() - 100000)
+                        # print('aaaaaaaaaaa')
+                        # print(result_change_fall)
+    print(f'计算总数={len(result) + len(result_change_fall)}')
+    if len(result) * len(result_change) * len(result_change_fall) != 0:
+        print(f'以{num}内最低值波动{Volatility}为支撑、MA5斜率为{rate}%,结果状态为:')
+        print('正盈利的个股为:', len(result), '成功率为:', len(result) / len(table_list))
+        print(
+            f'总盈利:{np.sum(result_change)} 平均盈利:{np.mean(result_change) / len(result)},最大盈利:{np.max(result_change)}, 最小盈利:{np.min(result_change)}')
+        print(
+            f'总亏损:{np.sum(result_change_fall)},平均亏损:{np.mean(result_change_fall) / len(result_change_fall)},最大亏损:{np.min(result_change_fall)} 最小亏损:{np.max(result_change_fall)}')
+
+        # '周期', '波动率', 'MA5斜率', '盈利个数', '盈利比例', '总盈利', '平均盈利', '最大盈利', '最小盈利', '总亏损', '平均亏损', '最大亏损', '最小亏损', '盈亏对比']
+        list_date.append([num, Volatility, rate, len(result), len(result) / len(table_list), np.nansum(result_change),
+                          np.nanmean(result_change), np.nanmax(result_change), np.min(result_change),
+                          np.nansum(result_change_fall), np.nanmean(result_change_fall),
+                          np.nanmin(result_change_fall), np.nanmax(result_change_fall),
+                          len(result_change) / len(result_change_fall)])
+        to_df(list_date)
+        endtime = dt.now()
+        print(f'{num}天波动率为{Volatility}%MA5斜率为{rate},myPID is {os.getpid()}.本轮耗时为{endtime - sttime}')
+    else:
+        print('阿欧', len(result), len(result_change), len(result_change_fall), num, Volatility, rate, err_list)
+        list_date.append([num, Volatility, rate, 0, len(result) / len(table_list), len(result),
+                          len(result), len(result), len(result), len(result), len(result), len(result), 0])
+    to_df(list_date)
+    # cerebro.plot()
+
+
+# df = pd.DataFrame(
+#     columns=['周期', '波动率', 'MA5斜率', '盈利个数', '盈利比例', '总盈利', '平均盈利', '最大盈利', '最小盈利', '总亏损',
+#              '平均亏损', '最大亏损', '最小亏损'])
+if __name__ == '__main__':
+    starttime = dt.now()
+    print(starttime)
+    pus = psutil.Process()
+    # pus.cpu_affinity([0, 1, 2, 3, 4, 5, 6, 7])
+    # pus.cpu_affinity([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15])
+    # print(type(platform.node()))
+    # engine = create_engine('mysql+pymysql://root:r6kEwqWU9!v3@localhost:3307/hlfx?charset=utf8', poolclass=NullPool)
+
+    # stocks = pd.read_sql_query(
+    #                     'select value from MA5_1d', engine_hlfx)
+
+    fre = '1d'
+    db = pymysql.connect(host='localhost',
+                         user='root',
+                         port=3307,
+                         password='r6kEwqWU9!v3',
+                         database='qmt_stocks_tech')
+    cursor = db.cursor()
+    cursor.execute("show tables like '%%%s%%' " % fre)
+    table_list = [tuple[0] for tuple in cursor.fetchall()]
+    # print(table_list)
+    # table_list = table_list[0:500]
+    print(f'计算个股数为:{len(table_list)}')
+
+    list_date = mp.Manager().list()
+    thread_list = []
+    # pool = mp.Pool(processes=mp.cpu_count())
+    pool = mp.Pool(processes=24)
+    for num in range(80, 200, 20):
+        for Volatility in range(3, 13, 1):
+            for rate in range(3, 13, 1):
+                # step = math.ceil(len(table_list) / mp.cpu_count())
+                result = []
+                result_change = []
+                result_change_fall = []
+                err_list = []
+                print(f'{num}天波动率为{Volatility}%MA5斜率为{rate}')
+                # for i in range(0, len(table_list), step):
+                stattime = dt.now()
+                # thd = threading.local()
+                # print(i)
+                # p = mp.Process(target=backtrader, args=(df, table_list, result, result_change, result_change_fall,
+                #                                         num, Volatility, rate, err_list))
+                # thread_list.append(p)
+                pool.apply_async(func=backtrader,
+                                 args=(list_date, table_list, result, result_change, result_change_fall,
+                                       num, Volatility, rate, err_list,), error_callback=err_call_back)
+    pool.close()
+    pool.join()
+
+    edtime = dt.now()
+    print('总耗时:', edtime - starttime)
+    # df.to_csv(r'C:\Users\Daniel\Documents\策略穷举2.csv', index=True)

+ 351 - 0
backtrader/230508_bt.py

@@ -0,0 +1,351 @@
+import os
+import traceback
+import numpy as np
+from sqlalchemy import create_engine
+import pandas as pd
+import pymysql
+import backtrader as bt
+import backtrader.indicators as btind
+import datetime
+import math
+from datetime import datetime as dt
+import multiprocessing as mp
+from backtrader.feeds import PandasData
+import platform
+import psutil
+
+
+# import multiprocessing
+# import matplotlib
+
+
+class MyPandasData(PandasData):
+    lines = ('hl', 'dif', 'dea', 'macd', 'rsi_6', 'rsi_12', 'rsi_24',)
+    params = (('hl', 7),
+              ('dif', 8),
+              ('dea', 9),
+              ('macd', 10),
+              ('rsi_6', 11),
+              ('rsi_12', 12),
+              ('rsi_24', 13),
+              )
+    '''
+    lines = ('change_pct', 'net_amount_main', 'net_pct_main', 'net_amount_xl', 'net_pct_xl', 'net_amount_l', 'net_pct_l'
+             , 'net_amount_m', 'net_pct_m', 'net_amount_s', 'net_pct_s',)
+    params = (('change_pct', 7),
+              ('net_amount_main', 8),
+              ('net_pct_main', 9),
+              ('net_amount_xl', 10),
+              ('net_pct_xl', 11),
+              ('net_amount_l', 12),
+              ('net_pct_l', 13),
+              ('net_amount_m', 14),
+              ('net_pct_m', 15),
+              ('net_amount_s', 16),
+              ('net_pct_s', 17),
+              )
+    '''
+
+
+class TestStrategy(bt.Strategy):
+    params = (
+        ("num", 3),
+        ('Volatility', 0),
+        ('rate', 3),  # 注意要有逗号!!
+    )
+
+    def log(self, txt, dt=None):
+        ''' Logging function for this strategy'''
+        dt = dt or self.datas[0].datetime.date(0)
+        # print('%s, %s' % (dt.isoformat(), txt))
+
+    def __init__(self):
+        # self.num = num
+        # self.Volatility = Volatility/100
+        # Keep a reference to the "close" line in the data[0] dataseries
+        self.pos_price = 0
+        self.dataclose = self.datas[0].close
+        self.dataopen = self.datas[0].open
+        self.high = self.datas[0].high
+        self.low = self.datas[0].low
+        self.volume = self.datas[0].volume
+        self.hl = self.datas[0].hl
+        self.dif = self.datas[0].dif
+        self.dea = self.datas[0].dea
+        self.macd = self.datas[0].macd
+        self.rsi_6 = self.datas[0].rsi_6
+        self.rsi_12 = self.datas[0].rsi_12
+        self.rsi_24 = self.datas[0].rsi_24
+        # self.change_pct = self.datas[0].change_pct
+        # self.net_amount_main = self.datas[0].net_amount_main
+        # self.net_pct_main = self.datas[0].net_pct_main
+        # self.net_amount_xl = self.datas[0].net_amount_xl
+        # self.net_pct_xl = self.datas[0].net_pct_xl
+        # self.net_amount_l = self.datas[0].net_amount_l
+        # self.net_pct_l = self.datas[0].net_pct_l
+        self.sma5 = btind.MovingAverageSimple(self.datas[0].close, period=5)
+        self.sma10 = btind.MovingAverageSimple(self.datas[0].close, period=10)
+        self.sma20 = btind.MovingAverageSimple(self.datas[0].close, period=20)
+        self.sma60 = btind.MovingAverageSimple(self.datas[0].close, period=60)
+
+    def notify_order(self, order):
+        """
+        订单状态处理
+
+        Arguments:
+            order {object} -- 订单状态
+        """
+        if order.status in [order.Submitted, order.Accepted]:
+            # 如订单已被处理,则不用做任何事情
+            return
+
+        # 检查订单是否完成
+        if order.status in [order.Completed]:
+            if order.isbuy():
+                self.buyprice = order.executed.price
+                self.buycomm = order.executed.comm
+            self.bar_executed = len(self)
+
+        # 订单因为缺少资金之类的原因被拒绝执行
+        elif order.status in [order.Canceled, order.Margin, order.Rejected]:
+            pass
+            # self.log('Order Canceled/Margin/Rejected')
+
+        # 订单状态处理完成,设为空
+        self.order = None
+
+    def notify_trade(self, trade):
+        """
+        交易成果
+
+        Arguments:
+            trade {object} -- 交易状态
+        """
+        if not trade.isclosed:
+            return
+
+        # 显示交易的毛利率和净利润
+        # self.log('OPERATION PROFIT, GROSS %.2f, NET %.2f' % (trade.pnl, trade.pnlcomm))
+
+    def next(self):
+        # print(self.num,self.Volatility)
+        # Simply log the closing price of the series from the reference
+        # self.sma20[-2] < self.sma20[-1] < self.sma20[0] and self.sma10[-2] < self.sma10[-1] < self.sma10[0]
+        # and (self.sma5[-1] < self.sma10[-1])
+        # and (self.net_pct_l[0] > 10) and (self.net_pct_xl[0] > 3)  \
+        # and (self.net_amount_main[-1] > 0) and (self.net_amount_main[0] > 0)
+
+        if len(self) > self.params.num:
+            vola = self.params.Volatility / 100
+            rate = self.params.rate / 100
+            lowest = np.min(self.low.get(size=self.params.num))
+            highest = np.max(self.high.get(size=self.params.num))
+
+            # > self.sma5[-1]
+            # and (((lowest * (1 - vola)) < self.low[-2] < (lowest * (1 + vola))) or (
+            #         (lowest * (1 - vola)) < self.low[-1] < (lowest * (1 + vola)))) \
+            if self.hl[-1] == 2 or self.hl[-1] == 1:
+                m = -2
+                # self.order = self.buy()
+                # self.pos_price = self.low[-1]
+
+                while True:
+                    if (self.hl[m] == 2 or self.hl[m] == 1) and self.macd[m] > self.macd[-1] \
+                            and 0.99 * self.sma10[m] < self.sma5[m] < 1.01 * self.sma10[m] \
+                            and 0.99 * self.sma10[m-1] < self.sma5[m-1] < 1.01 * self.sma10[m-1] \
+                            and 0.99 * self.sma10[m-2] < self.sma5[m-2] < 1.01 * self.sma10[m-2] \
+                            and self.dataclose[0] > self.sma5[0] \
+                            and self.dataclose[-1] > self.dataopen[-1] \
+                            and self.volume[-1] > self.volume[-2] \
+                            and (self.sma10[-2] - self.sma5[-2]) < (self.sma10[-1] - self.sma5[-1]) \
+                            and self.low[-2] < self.sma5[-2] * (1 - rate) \
+                            and self.sma5[-1] < self.sma10[-1] < self.sma20[-1] < self.sma20[-2] < self.sma20[-3]:
+                        self.order = self.buy()
+                        self.pos_price = self.low[-1]
+                        break
+                    m -= 1
+                    if m + len(self) == 2:
+                        break
+
+            # elif (self.hl[0] == 5 or self.dataclose[0] < self.sma5[0]):
+            elif self.dataclose[0] < self.sma5[0] or self.sma5[0] < self.sma5[-1] \
+                    or self.dataclose[0] < self.pos_price or self.high[0] > self.sma5[0] * (1 + vola):
+                self.order = self.close()
+                self.pos_price = 0
+
+    def stop(self):
+        # pass
+        self.log(u'(MA趋势交易效果) Ending Value %.2f' % (self.broker.getvalue()))
+
+
+def err_call_back(err):
+    print(f'出错啦~ error:{str(err)}')
+    traceback.format_exc(err)
+
+
+def to_df(lt):
+    print('开始存数据')
+    df = pd.DataFrame(list(lt),
+                      columns=['周期', '波动率', 'MA5斜率', '盈利个数', '盈利比例', '总盈利', '平均盈利', '最大盈利',
+                               '最小盈利', '总亏损', '平均亏损', '最大亏损', '最小亏损', '盈亏对比'])
+    df.sort_values(by=['周期', '波动率', 'MA5斜率'], ascending=True, inplace=True)
+    df = df.reset_index(drop=True)
+    if platform.node() == 'DanieldeMBP.lan':
+        df.to_csv(f"/Users/daniel/Documents/策略/策略穷举-均线粘连后底分型{dt.now().strftime('%Y%m%d%H%m%S')}.csv", index=True,
+                  encoding='utf_8_sig', mode='w')
+    else:
+        df.to_csv(f"C:\Daniel\策略\策略穷举底分型_均线缠绕_只买一次{dt.now().strftime('%Y%m%d%H%m%S')}.csv", index=True,
+                  encoding='utf_8_sig', mode='w')
+    print(f'结果:, \n, {df}')
+
+
+def backtrader(list_date, table_list, result, result_change, result_change_fall, num, Volatility, rate, err_list):
+    print(f'{num}天波动率为{Volatility}%MA5斜率为{rate}', 'myPID is ', os.getpid())
+    sttime = dt.now()
+    engine = create_engine('mysql+pymysql://root:r6kEwqWU9!v3@localhost:3307/qmt_stocks_tech?charset=utf8',
+                           max_overflow=-1)
+    for stock in table_list:
+        # print(stock)
+        stk_df = pd.read_sql_table(stock, engine.connect())
+        stk_df.time = pd.to_datetime(stk_df.time)
+        # stk_df = stk_df[stk_df['HL'] != '-']
+        try:
+            stk_df['HL'] = stk_df['HL'].map({'L': 1,
+                                             'LL': 2,
+                                             'L*': 3,
+                                             'H': 4,
+                                             'HH': 5,
+                                             'H*': 6,
+                                             '-': 7})
+        except BaseException:
+            print(f'{stock}数据不全,不做测试')
+        else:
+            if len(stk_df) > 60:
+                cerebro = bt.Cerebro()
+                cerebro.addstrategy(TestStrategy, num=num, Volatility=Volatility, rate=rate)
+                cerebro.addsizer(bt.sizers.FixedSize, stake=10000)
+                data = MyPandasData(dataname=stk_df,
+                                    fromdate=datetime.datetime(2017, 1, 1),
+                                    todate=datetime.datetime(2022, 10, 30),
+                                    datetime='time',
+                                    open='open_back',
+                                    close='close_back',
+                                    high='high_back',
+                                    low='low_back',
+                                    volume='volume_back',
+                                    hl='HL',
+                                    dif='dif',
+                                    dea='dea',
+                                    macd='macd',
+                                    rsi_6='rsi_6',
+                                    rsi_12='rsi_12',
+                                    rsi_24='rsi_24',
+                                    )
+                # print('取值完成')
+                cerebro.adddata(data, name=stock)
+                cerebro.broker.setcash(100000.0)
+                cerebro.broker.setcommission(0.005)
+                cerebro.addanalyzer(bt.analyzers.PyFolio)
+                # 策略执行前的资金
+                # print('启动资金: %.2f' % cerebro.broker.getvalue())
+                try:
+                    # 策略执行
+                    cerebro.run()
+                except IndexError as e:
+                    err_list.append(stock)
+                    # print(f'{num}天波动率为{Volatility}%MA5斜率为{rate}的{stock}错误')
+                    # print(e)
+                else:
+                    if cerebro.broker.getvalue() > 100000.0:
+                        result_change.append(cerebro.broker.getvalue() - 100000)
+                        result.append(stock)
+                        # print('recode!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!')
+                        # print(result)
+                    elif cerebro.broker.getvalue() < 100000.0:
+                        result_change_fall.append(cerebro.broker.getvalue() - 100000)
+                        # print('aaaaaaaaaaa')
+                        # print(result_change_fall)
+    print(f'计算总数={len(result) + len(result_change_fall)}')
+    if len(result) * len(result_change) * len(result_change_fall) != 0:
+        print(f'以{num}内最低值波动{Volatility}为支撑、MA5斜率为{rate}%,结果状态为:')
+        print('正盈利的个股为:', len(result), '成功率为:', len(result) / len(table_list))
+        print(
+            f'总盈利:{np.sum(result_change)} 平均盈利:{np.mean(result_change) / len(result)},最大盈利:{np.max(result_change)}, 最小盈利:{np.min(result_change)}')
+        print(
+            f'总亏损:{np.sum(result_change_fall)},平均亏损:{np.mean(result_change_fall) / len(result_change_fall)},最大亏损:{np.min(result_change_fall)} 最小亏损:{np.max(result_change_fall)}')
+
+        # '周期', '波动率', 'MA5斜率', '盈利个数', '盈利比例', '总盈利', '平均盈利', '最大盈利', '最小盈利', '总亏损', '平均亏损', '最大亏损', '最小亏损', '盈亏对比']
+        list_date.append([num, Volatility, rate, len(result), len(result) / len(table_list), np.nansum(result_change),
+                          np.nanmean(result_change), np.nanmax(result_change), np.min(result_change),
+                          np.nansum(result_change_fall), np.nanmean(result_change_fall),
+                          np.nanmin(result_change_fall), np.nanmax(result_change_fall),
+                          len(result_change) / len(result_change_fall)])
+        to_df(list_date)
+        endtime = dt.now()
+        print(f'{num}天波动率为{Volatility}%MA5斜率为{rate},myPID is {os.getpid()}.本轮耗时为{endtime - sttime}')
+    else:
+        print('阿欧', len(result), len(result_change), len(result_change_fall), num, Volatility, rate, err_list)
+        list_date.append([num, Volatility, rate, 0, len(result) / len(table_list), len(result),
+                          len(result), len(result), len(result), len(result), len(result), len(result), 0])
+    to_df(list_date)
+    # cerebro.plot()
+
+
+# df = pd.DataFrame(
+#     columns=['周期', '波动率', 'MA5斜率', '盈利个数', '盈利比例', '总盈利', '平均盈利', '最大盈利', '最小盈利', '总亏损',
+#              '平均亏损', '最大亏损', '最小亏损'])
+if __name__ == '__main__':
+    starttime = dt.now()
+    print(starttime)
+    pus = psutil.Process()
+    # pus.cpu_affinity([23, 16, 17, 18, 19, 20, 21, 22])
+    # pus.cpu_affinity([0, 1, 2, 3, 4, 5, 6, 7])
+    # pus.cpu_affinity([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15])
+    # print(type(platform.node()))
+    # engine = create_engine('mysql+pymysql://root:r6kEwqWU9!v3@localhost:3307/hlfx?charset=utf8', poolclass=NullPool)
+
+    # stocks = pd.read_sql_query(
+    #                     'select value from MA5_1d', engine_hlfx)
+
+    fre = '1d'
+    db = pymysql.connect(host='localhost',
+                         user='root',
+                         port=3307,
+                         password='r6kEwqWU9!v3',
+                         database='qmt_stocks_tech')
+    cursor = db.cursor()
+    cursor.execute("show tables like '%%%s%%' " % fre)
+    table_list = [tuple[0] for tuple in cursor.fetchall()]
+    # print(table_list)
+    # table_list = table_list[0:500]
+    print(f'计算个股数为:{len(table_list)}')
+
+    list_date = mp.Manager().list()
+    thread_list = []
+    pool = mp.Pool(processes=mp.cpu_count())
+    # pool = mp.Pool(processes=8)
+    for num in range(60, 80, 20):
+        for Volatility in range(7, 13, 1):
+            for rate in range(3, 13, 1):
+                # step = math.ceil(len(table_list) / mp.cpu_count())
+                result = []
+                result_change = []
+                result_change_fall = []
+                err_list = []
+                print(f'{num}天波动率为{Volatility}%MA5斜率为{rate}')
+                # for i in range(0, len(table_list), step):
+                stattime = dt.now()
+                # thd = threading.local()
+                # print(i)
+                # p = mp.Process(target=backtrader, args=(df, table_list, result, result_change, result_change_fall,
+                #                                         num, Volatility, rate, err_list))
+                # thread_list.append(p)
+                pool.apply_async(func=backtrader,
+                                 args=(list_date, table_list, result, result_change, result_change_fall,
+                                       num, Volatility, rate, err_list,), error_callback=err_call_back)
+    pool.close()
+    pool.join()
+
+    edtime = dt.now()
+    print('总耗时:', edtime - starttime)
+    # df.to_csv(r'C:\Users\Daniel\Documents\策略穷举2.csv', index=True)

+ 113 - 0
docker.start.txt

@@ -6,3 +6,116 @@ docker run -itd --name mysql82 -p 3312:3306 -e MYSQL_ROOT_PASSWORD=r6kEwqWU9!v3
 mysqldump -uroot -pr6kEwqWU9!v3 -P3307 --databases qmt_stocks_whole >d:/qmt_stocks_whole.sql
 
 # version order 1
+
+
+docker run -itd --name mysql8 -p 3307:3306 -e MYSQL_ROOT_PASSWORD=r6kEwqWU9!v3 -v C:/docker_mysql/stock_data:/var/lib/mysql -v C:/docker_mysql/config/mysql.cnf:/etc/my.cnf daocloud.io/library/mysql:8.0.21
+
+docker run -itd --name mysql8033 -p 3308:3306 -e MYSQL_ROOT_PASSWORD=r6kEwqWU9!v3 -v C:/docker_mysql/0833:/var/lib/mysql -v C:/docker_mysql/conf:/etc/mysql mysql:8.0.33
+
+docker run -itd --name mysql8033 -p 3308:3306 -e MYSQL_ROOT_PASSWORD=r6kEwqWU9!v3 -v C:/docker_mysql/mysql.cnf:/etc/my.cnf mysql:8.0.33-debian
+
+docker run  -itd  -p 3307:3306 --name mysql8033 -e character-set-server=utf8mb4 --privileged=true  --restart unless-stopped -v C:/docker_mysql/stock_data:/var/lib/mysql  -e MYSQL_ROOT_PASSWORD=r6kEwqWU9!v3  -d mysql:8.0.33 --skip-log-bin --disable-log-bin --lower_case_table_names=1
+
+
+docker run  -itd  -p 3307:3306 --name mysql8033 -e character-set-server=utf8mb4 --privileged=true  --restart unless-stopped -v C:/docker_mysql/stock_data:/var/lib/mysql  -e MYSQL_ROOT_PASSWORD=r6kEwqWU9!v3  -d mysql:8.0.33 --lower_case_table_names=1 --skip-log-bin --disable-log-bin
+
+
+
+
+20230515
+docker run  -itd  -p 3307:3306 --name mysql8033 --privileged=true  --restart unless-stopped  -v C:/docker_mysql/stock_data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=r6kEwqWU9!v3  -d mysql:8.0.33
+docker run  -itd  -p 3309:3306 --name mysqltest -e character-set-server=utf8mb4 --privileged=true  --restart unless-stopped   -e MYSQL_ROOT_PASSWORD=r6kEwqWU9!v3  -d mysql:8.0.33 --skip-log-bin --disable-log-bin --log-error=/var/lib/mysql/err.log
+
+
+
+# For advice on how to change settings please see
+# http://dev.mysql.com/doc/refman/8.0/en/server-configuration-defaults.html
+
+[mysqld]
+#
+# Remove leading # and set to the amount of RAM for the most important data
+# cache in MySQL. Start at 70% of total RAM for dedicated server, else 10%.
+# innodb_buffer_pool_size = 128M
+#
+# Remove leading # to turn on a very important data integrity option: logging
+# changes to the binary log between backups.
+# log_bin
+#
+# Remove leading # to set options mainly useful for reporting servers.
+# The server defaults are faster for transactions and fast SELECTs.
+# Adjust sizes as needed, experiment to find the optimal values.
+# join_buffer_size = 128M
+# sort_buffer_size = 2M
+# read_rnd_buffer_size = 2M
+
+# Remove leading # to revert to previous value for default_authentication_plugin,
+# this will increase compatibility with older clients. For background, see:
+# https://dev.mysql.com/doc/refman/8.0/en/server-system-variables.html#sysvar_default_authentication_plugin
+# default-authentication-plugin=mysql_native_password
+skip-log-bin
+disable-log-bin
+skip-host-cache
+skip-name-resolve
+datadir=/var/lib/mysql
+socket=/var/run/mysqld/mysqld.sock
+secure-file-priv=/var/lib/mysql-files
+user=mysql
+max_connections=10000
+max_user_connections=5000
+character-set-server=utf8mb4
+thread_stack=4096k
+innodb_log_file_size=200M
+
+pid-file=/var/run/mysqld/mysqld.pid
+[client]
+socket=/var/run/mysqld/mysqld.sock
+character-set-server=utf8mb4
+!includedir /etc/mysql/conf.d/
+
+
+
+
+
+# For advice on how to change settings please see
+# http://dev.mysql.com/doc/refman/8.0/en/server-configuration-defaults.html
+
+[mysqld]
+#
+# Remove leading # and set to the amount of RAM for the most important data
+# cache in MySQL. Start at 70% of total RAM for dedicated server, else 10%.
+# innodb_buffer_pool_size = 128M
+#
+# Remove leading # to turn on a very important data integrity option: logging
+# changes to the binary log between backups.
+# log_bin
+#
+# Remove leading # to set options mainly useful for reporting servers.
+# The server defaults are faster for transactions and fast SELECTs.
+# Adjust sizes as needed, experiment to find the optimal values.
+# join_buffer_size = 128M
+# sort_buffer_size = 2M
+# read_rnd_buffer_size = 2M
+
+# Remove leading # to revert to previous value for default_authentication_plugin,
+# this will increase compatibility with older clients. For background, see:
+# https://dev.mysql.com/doc/refman/8.0/en/server-system-variables.html#sysvar_default_authentication_plugin
+# default-authentication-plugin=mysql_native_password
+skip-host-cache
+skip-name-resolve
+datadir=/var/lib/mysql
+socket=/var/run/mysqld/mysqld.sock
+secure-file-priv=/var/lib/mysql-files
+log_error=/var/lib/mysql/error.log
+user=mysql
+max_connections=2000
+max_user_connections=1000
+max_connect_errors=1000000
+thread_cache_size=300
+server-id = 1
+character-set-server = utf8mb4
+
+pid-file=/var/run/mysqld/mysqld.pid
+[client]
+socket=/var/run/mysqld/mysqld.sock
+
+!includedir /etc/mysql/conf.d/

+ 90 - 0
ttt.py

@@ -0,0 +1,90 @@
+m = 0
+# def mm(m):
+#     while True:
+#         print('m=', m)
+#         m = m + 1
+#         if m == 10:
+#             break
+#     return
+# print(mm(m))
+
+import pandas as pd
+import pymysql
+from sqlalchemy import create_engine, text
+import threading
+from datetime import datetime as dt
+import datetime
+from jqdatasdk.technical_analysis import *
+from xtquant import xtdata, xtconstant
+from xtquant.xttype import StockAccount
+from xtquant.xttrader import XtQuantTrader, XtQuantTraderCallback
+import time
+import math
+import multiprocessing as mp
+import os
+import psutil
+import traceback
+from apscheduler.schedulers.blocking import BlockingScheduler
+import sys
+
+# p = xtdata.get_instrument_detail('000001.sz')
+# print(p.get('UpStopPrice'))
+
+
+# stock = '000001.SZ'
+# engine_stock = create_engine('mysql+pymysql://root:r6kEwqWU9!v3@localhost:3307/qmt_stocks_whole?charset=utf8',
+#                              pool_size=5000, pool_recycle=50, max_overflow=-1)
+# df = pd.read_sql_query(text(
+#                 'select close_front, high_front from `%s_1d`' % stock), engine_stock.connect())
+#
+# print(df['close_front'].iloc[-1])
+# print(df['close_front'].iloc[-2])
+
+
+def sell_trader(data):
+    stock = '002645.SZ'
+    # print(data[stock][0]['close'])
+    print(xtdata.get_instrument_detail(stock).get('UpStopPrice'))
+
+    current_price = data[stock]['lastPrice']
+    print('aaa', current_price, data[stock]['time'])
+    print(data[stock])
+
+'''
+    if stock in data and can_use_volume != 0:
+        current_price = data[stock]['lastPrice']
+        open_price = data[stock]['open']
+        MA5 = ma(stock, 5, data)
+        MA5_1 = ma_1(stock, 5)
+        df = pd.read_sql_query(text(
+            'select close_front, high_front from `%s_1d`' % stock), engine_stock.connect())
+        print(f'{stock},持仓量为{can_use_volume}当前价:{current_price},MA5:{MA5},昨日MA5:{MA5_1},开始判断:')
+        if current_price == xtdata.get_instrument_detail(stock).get('UpStopPrice') \
+                or (df['close_front'].iloc[-1] == df['high_front'].iloc[-1]
+                    and df['close_front'].iloc[-1] / df['close_front'].iloc[-2] > 1.08):
+            continue
+        elif current_price < MA5 or MA5 < MA5_1:
+            print('卖出信号!!!!!!', stock, current_price)
+            order_id = xt_trader.order_stock(acc, stock, xtconstant.STOCK_SELL, can_use_volume,
+                                             xtconstant.LATEST_PRICE, 0, 'MA5策略', '低于MA5趋势向下')
+            print('价格:', current_price, open_price, MA5, MA5_1, '低于MA5趋势向下')
+            print(order_id, stock, can_use_volume)
+        elif current_price > MA5 * 1.07:
+            print('盈利乖离率超7%!!!!!!', stock, current_price)
+            order_id = xt_trader.order_stock(acc, stock, xtconstant.STOCK_SELL, can_use_volume,
+                                             xtconstant.LATEST_PRICE, 0, 'MA5策略', '盈利乖离率超7%')
+            print('价格:', current_price, open_price, MA5, MA5_1, '盈利乖离率超7%')
+            print(order_id, stock, can_use_volume)
+    else:
+        print(f'本轮没有持仓股票信息!')
+'''
+
+
+
+
+# xtdata.subscribe_quote('301125.SZ',callback=sell_trader)
+stocks = xtdata.get_stock_list_in_sector('沪深A股')
+xtdata.download_history_data2(stock_list=stocks, period='30m', start_time='', end_time='')
+print(stocks)
+seq = xtdata.subscribe_whole_quote(stocks, callback=sell_trader)
+xtdata.run()