当前位置: 首页 > 工具软件 > AKShare > 使用案例 >

akshare 配对策略

蒋泰
2023-12-01
from __future__ import (absolute_import, division, print_function,
unicode_literals)
import argparse
from datetime import  datetime
import pandas as pd
#from pylab import mpl
import numpy as np
# The above could be sent to an independent module
import backtrader as bt
import backtrader.feeds as btfeeds
import backtrader.indicators as btind
import akshare as ak

class PairTradingStrategy(bt.Strategy):
    params = dict(
    period=10,
    stake=10,
    qty1=0,
    qty2=0,
    printout=True,

    #设置上限为2.1
    upper=2.1,

    #设置下限为-2.1
    lower=-2.1,
    up_medium=0.5,
    low_medium=-0.5,
    status=0,
    portfolio_value=10000,
    )

    def log(self, txt, dt=None):
        if self.p.printout:
            dt = dt or self.data.datetime[0]
            dt = bt.num2date(dt)
        print('%s, %s' % (dt.isoformat(), txt))

    def notify_order(self, order):
        if order.status in [bt.Order.Submitted, bt.Order.Accepted]:
            return # Await further notifications

        if order.status == order.Completed:
            if order.isbuy():
                buytxt = 'BUY COMPLETE, %.2f' % order.executed.price
                self.log(buytxt, order.executed.dt)
            else:
                selltxt = 'SELL COMPLETE, %.2f' % order.executed.price
                self.log(selltxt, order.executed.dt)

        elif order.status in [order.Expired, order.Canceled, order.Margin]:
            self.log('%s ,' % order.Status[order.status])
            pass # Simply log

            # Allow new orders  允许开新单
            self.orderid = None

    def __init__(self):
        # To control operation entries
        self.orderid = None
        self.qty1 = self.p.qty1
        self.qty2 = self.p.qty2
        self.upper_limit = self.p.upper
        self.lower_limit = self.p.lower
        self.up_medium = self.p.up_medium
        self.low_medium = self.p.low_medium
        self.status = self.p.status
        self.portfolio_value = self.p.portfolio_value

    # Signals performed with PD.OLS :  计算data0上data1的回归和zscord值
        self.transform = btind.OLS_TransformationN(self.data0, self.data1,
        period=self.p.period)
        self.zscore = self.transform.zscore

    # Checking signals built with StatsModel.API :
#         self.ols_transfo = btind.OLS_Transformation(self.data0, self.data1,
#         period=self.p.period,
#         plot=True)

    def next(self):

        if self.orderid:
            return # if an order is active, no new orders are allowed

        if self.p.printout:
            print('Self len:', len(self))
            print('Data0 len:', len(self.data0))
            print('Data1 len:', len(self.data1))
            print('Data0 len == Data1 len:',
            len(self.data0) == len(self.data1))

            print('Data0 dt:', self.data0.datetime.datetime())
            print('Data1 dt:', self.data1.datetime.datetime())

        print('status is', self.status)
        print('zscore is', self.zscore[0])

        # Step 2: Check conditions for SHORT & place the order 检查是否需要卖出
        # Checking the condition for SHORT
        if (self.zscore[0] > self.upper_limit) and (self.status != 1):

            # Calculating the number of shares for each stock   计算股票的数量
            value = 0.5 * self.portfolio_value # Divide the cash equally
            x = int(value / (self.data0.close)) # Find the number of shares for Stock1
            y = int(value / (self.data1.close)) # Find the number of shares for Stock2
            print('x + self.qty1 is', x + self.qty1)
            print('y + self.qty2 is', y + self.qty2)

            # Placing the order  下单,卖1买2
            self.log('SELL CREATE %s, price = %.2f, qty = %d' % ("PEP", self.data0.close[0], x + self.qty1))
            self.sell(data=self.data0, size=(x + self.qty1)) # Place an order for buying y + qty2 shares
            self.log('BUY CREATE %s, price = %.2f, qty = %d' % ("KO", self.data1.close[0], y + self.qty2))
            self.buy(data=self.data1, size=(y + self.qty2)) # Place an order for selling x + qty1 shares

            # Updating the counters with new value  更新持仓值
            self.qty1 = x # The new open position quantity for Stock1 is x shares
            self.qty2 = y # The new open position quantity for Stock2 is y shares

            self.status = 1 # The current status is "short the spread"

        # Step 3: Check conditions for LONG & place the order  检查是否买入
        # Checking the condition for LONG
        elif (self.zscore[0] < self.lower_limit) and (self.status != 2):

    # Calculating the number of shares for each stock  计算买入数量
            value = 0.5 * self.portfolio_value # Divide the cash equally
            x = int(value / (self.data0.close)) # Find the number of shares for Stock1
            y = int(value / (self.data1.close)) # Find the number of shares for Stock2
            print('x + self.qty1 is', x + self.qty1)
            print('y + self.qty2 is', y + self.qty2)

    # Place the order  下单买入股票1,卖出股票2
            self.log('BUY CREATE %s, price = %.2f, qty = %d' % ("PEP", self.data0.close[0], x + self.qty1))
            self.buy(data=self.data0, size=(x + self.qty1)) # Place an order for buying x + qty1 shares
            self.log('SELL CREATE %s, price = %.2f, qty = %d' % ("KO", self.data1.close[0], y + self.qty2))
            self.sell(data=self.data1, size=(y + self.qty2)) # Place an order for selling y + qty2 shares

            # Updating the counters with new value  刷新持仓值
            self.qty1 = x # The new open position quantity for Stock1 is x shares
            self.qty2 = y # The new open position quantity for Stock2 is y shares
            self.status = 2 # The current status is "long the spread"


    # Step 4: Check conditions for No Trade 检查是否无需交易
    # If the z-score is within the two bounds, close all

        elif (self.zscore[0] < self.up_medium and self.zscore[0] > self.low_medium):
            self.log('CLOSE LONG %s, price = %.2f' % ("PEP", self.data0.close[0]))
            self.close(self.data0)
            self.log('CLOSE LONG %s, price = %.2f' % ("KO", self.data1.close[0]))
            self.close(self.data1)


    def stop(self):
        print('==================================================')
        print('Starting Value - %.2f' % self.broker.startingcash)
        print('Ending Value - %.2f' % self.broker.getvalue())
        print('==================================================')

def parse_args():
    parser = argparse.ArgumentParser(description='MultiData Strategy')

    parser.add_argument('--data0', '-d0',
                        default='../../datas/daily-PEP.csv',
                        help='1st data into the system')

    parser.add_argument('--data1', '-d1',
                        default='../../datas/daily-KO.csv',
                        help='2nd data into the system')

    parser.add_argument('--fromdate', '-f',
                        default='1997-01-01',
                        help='Starting date in YYYY-MM-DD format')

    parser.add_argument('--todate', '-t',
                        default='1998-06-01',
                        help='Starting date in YYYY-MM-DD format')

    parser.add_argument('--period', default=10, type=int,
                        help='Period to apply to the Simple Moving Average')

    parser.add_argument('--cash', default=100000, type=int,
                        help='Starting Cash')

    parser.add_argument('--runnext', action='store_true',
                        help='Use next by next instead of runonce')

    parser.add_argument('--nopreload', action='store_true',
                        help='Do not preload the data')

    parser.add_argument('--oldsync', action='store_true',
                        help='Use old data synchronization method')

    parser.add_argument('--commperc', default=0.005, type=float,
                        help='Percentage commission (0.005 is 0.5%%')

    parser.add_argument('--stake', default=10, type=int,
                        help='Stake to apply in each operation')

    parser.add_argument('--plot', '-p', default=True, action='store_true',
                        help='Plot the read data')

    parser.add_argument('--numfigs', '-n', default=1,
                        help='Plot using numfigs figures')

    return parser.parse_args()
def runstrategy():
    args = parse_args()
    # Create a cerebro  建立大脑
    cerebro = bt.Cerebro()

    # Get the dates from the args  从csv取数据
    stock_hfq_df = ak.stock_zh_a_hist(symbol="000001", adjust="hfq").iloc[:, :6]

    # 处理字段命名,以符合 Backtrader 的要求
    stock_hfq_df.columns = ['date', 'open','close','high','low','volume',]
    # 把 date 作为日期索引,以符合 Backtrader 的要求
    stock_hfq_df.index = pd.to_datetime(stock_hfq_df['date'])
    start_date = datetime(2021, 4, 3)  # 回测开始时间
    end_date = datetime(2022, 6, 16)  # 回测结束时间
    data0 = bt.feeds.PandasData(dataname=stock_hfq_df,
                           fromdate=start_date,
                           todate=end_date)  # 加载数据


    stock_hfq_df = ak.stock_zh_a_hist(symbol="000002", adjust="hfq").iloc[:, :6]

    # 处理字段命名,以符合 Backtrader 的要求
    stock_hfq_df.columns = ['date', 'open','close','high','low','volume',]
    # 把 date 作为日期索引,以符合 Backtrader 的要求
    stock_hfq_df.index = pd.to_datetime(stock_hfq_df['date'])
    data1 = bt.feeds.PandasData(dataname=stock_hfq_df,
                           fromdate=start_date,
                           todate=end_date)  # 加载数据

    # Add the 1st data to cerebro  引入数据
    cerebro.adddata(data0)

    # Add the 2nd data to cerebro
    cerebro.adddata(data1)

    # Add the strategy  引入策略
    cerebro.addstrategy(PairTradingStrategy,
    period=args.period,
    stake=args.stake)

    # Add the commission - only stocks like a for each operation  假如资金量
    cerebro.broker.setcash(args.cash)

    # Add the commission - only stocks like a for each operation  引入佣金率
    cerebro.broker.setcommission(commission=args.commperc)

    # And run it  运行
#     cerebro.run(runonce=not args.runnext,
#                 preload=not args.nopreload,
#                 oldsync=args.oldsync)
    cerebro.run()
    # Plot if requested
    if args.plot:
        cerebro.plot(numfigs=args.numfigs, volume=False, zdown=False)

if __name__ == '__main__':
    runstrategy()

 类似资料: