策略3:动态再平衡----附python源代码

系列文章:

  1. 策略1:使用python+mongodb实现股票网格交易代码----附python源码
  2. 使用matplotlib画k线(2条k线同列)----附python源码
  3. 策略3:动态再平衡----附python源代码

思路:

  1. 选取2只股票或基金
  2. 在某一日,按照次日价格买入,买入的比例是1:1;各买入的金额是预计投入的总金额的一半
  3. 在每个周五,根据2只股票的市值(份额 * 周五收盘价),进行一次再平衡(市值高的卖出,市值低的买入,卖出和买入金额相同),
    以此保证2只股票的市值比例仍旧是1:1
  4. 测算从x到y日期,一直进行再平衡,所获取的最终的收益情况
import math
import pandas as pd

from utils.mongo_util.MongoUtil import MongoUtil


class ReBalance:

    def __init__(self) -> None:
        super().__init__()
        # 测算的开始时间
        self.start_date = "2018-01-02"
        self.meta_info = {
            # 初始本金
            "total_money": 100000,
            # 构造mongodb数据的查询条件
            "start_condition": {"date": {"$gte": self.start_date}},
            # 要进行再平衡测算的2只股票代码
            "security": ["SH510300", "SH601939"],
            # 存放上述2只股票代码的dataframe形式的历史收盘价格信息
            "history": []
        }
        # 每次进行再平衡后的市值金额和份额信息,[0]下标代表第一只股票,[1]下标代表第二只股票
        self.hold_info = [
            {"capital": 0, "shares": 0},
            {"capital": 0, "shares": 0}
        ]
        # 类似{'date': '2018-01-08', 'security': 'SH510300', 'direction': '卖出', 'close': 3.9056,
        # 'shares_before': 13100, 'shares_trans': 300, 'shares_remain': 12800,
        # 'capital_before': 50335.44, 'capital': 49991.68}
        # 用于记录每次再平衡时的买入或者卖出的交易信息,格式如上
        self.transactions = []
        self.today_info = {"date": ""}
        self.mongo_util = MongoUtil()

    def start(self):
        self.load_data_2_df()
        count = 1
        for today_date in self.meta_info['history'][0].index:
            # 下面的5,就是间隔周期,间隔多少天进行一次再平衡操作
            if count % 5 == 0 or count == 1:
                print("-------------------------------------------")
                self.today_info['date'] = today_date
                row1 = self.meta_info['history'][0].loc[today_date]
                row2 = self.meta_info['history'][1].loc[today_date]
                price1 = row1['close']
                price2 = row2['close']
                last_shares1 = self.hold_info[0]['shares']
                last_shares2 = self.hold_info[1]['shares']
                if today_date == self.start_date:
                    # 第一天只执行买入
                    trans_shares1 = self.cal_shares_by_price(self.meta_info['total_money'] / 2, price1)
                    trans_shares2 = self.cal_shares_by_price(self.meta_info['total_money'] / 2, price2)
                    self.operate(price1, trans_shares1, "买入", price2, trans_shares2, "买入")
                else:
                    # 非第一天,开始运算
                    self.rebalance(price1, price2, last_shares1, last_shares2)
            count += 1

    def load_data_2_df(self):
        # 从mongodb中读取数据到dataframe
        for security in self.meta_info['security']:
            coll_name = self.mongo_util.mongo_client['admin'][security]
            df = pd.DataFrame(list(coll_name.find(self.meta_info['start_condition']).sort("date", 1)))
            df.set_index('date', inplace=True)
            self.meta_info['history'].append(df)

    def cal_shares_by_price(self, capital, current_price):
        """
        总结:根据股价,计算买入的份额 = math.floor(1000/股价/100)*100
        :param current_price:
        :return:
        """
        return math.ceil(capital / current_price / 100) * 100

    def rebalance(self, price1, price2, last_shares1, last_shares2):
        gap = price1 * last_shares1 - price2 * last_shares2
        trans_shares1 = self.cal_shares_by_price(gap / 2, price1)
        remain_shares1 = last_shares1 - trans_shares1
        new_capital1 = (last_shares1 - trans_shares1) * price1
        print("本次预计 交易份额1:%s, 原份额:%s, 剩余份额1:%s, 重置后本金1:%s" % (str(-trans_shares1), str(last_shares1), str(remain_shares1), str(new_capital1)))
        trans_shares2 = self.cal_shares_by_price(gap / 2, price2)
        remain_shares2 = last_shares2 + trans_shares2
        new_capital2 = (last_shares2 + trans_shares2) * price2
        print("本次预计 交易份额2:%s, 原份额:%s, 剩余份额2:%s, 重置后本金2:%s" % (str(trans_shares2), str(last_shares2), str(remain_shares2), str(new_capital2)))
        if trans_shares1 == 0 or trans_shares2 == 0:
            print("再平衡份额较小,本次不执行")
            return
        if remain_shares1 < 0 or remain_shares2 < 0:
            print("再平衡后将没有持仓,本次不执行")
            return
        if gap > 0:
            self.operate(price1, trans_shares1, "卖出", price2, trans_shares2, "买入")
        else:
            self.operate(price1, -trans_shares1, "买入", price2, -trans_shares2, "卖出")

    def operate(self, price1, trans_shares1, direction1, price2, trans_shares2, direction2):
        if direction1 == '卖出':
            shares1 = self.hold_info[0]['shares'] - trans_shares1
        else:
            shares1 = self.hold_info[0]['shares'] + trans_shares1
        if direction2 == '卖出':
            shares2 = self.hold_info[1]['shares'] - trans_shares2
        else:
            shares2 = self.hold_info[1]['shares'] + trans_shares2
        trans1 = {
            "uuid": self.meta_info['security'][0] + "|" + self.today_info['date'],
            "date": self.today_info['date'],
            "security": self.meta_info['security'][0],
            "direction": direction1,
            "close": price1,
            "shares_before": self.hold_info[0]['shares'],
            "shares_trans": trans_shares1,
            "shares_remain": shares1,
            "capital_before": self.hold_info[0]['capital'],
            "capital": price1 * shares1
        }
        self.hold_info[0]['capital'] = price1 * shares1
        self.hold_info[0]['shares'] = shares1
        trans2 = {
            "uuid": self.meta_info['security'][1] + "|" + self.today_info['date'],
            "date": self.today_info['date'],
            "security": self.meta_info['security'][1],
            "direction": direction2,
            "close": price2,
            "shares_before": self.hold_info[1]['shares'],
            "shares_trans": trans_shares2,
            "shares_remain": shares2,
            "capital_before": self.hold_info[1]['capital'],
            "capital": price2 * shares2
        }
        self.hold_info[1]['capital'] = price2 * shares2
        self.hold_info[1]['shares'] = shares2
        self.transactions.append(trans1)
        self.transactions.append(trans2)
        # 将本次交易数据存储到MongoDB
        print(trans1)
        print(trans2)
        self.mongo_util.save_or_update_by_uuid(mongodb_collection="动态再平衡", data_dict=trans1)
        self.mongo_util.save_or_update_by_uuid(mongodb_collection="动态再平衡", data_dict=trans2)


##############################################################
reBalance = ReBalance()
# 每次测算前,先清理掉上一次测算的结果
reBalance.mongo_util.mongo_client['admin']['动态再平衡'].drop()
reBalance.start()



测算结果明细excel:
在这里插入图片描述

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值