摘要:林立在上海一家广告公司担任宣传策划,月收入5000元。他的妻子在一家咨询公司工作,月收入4000元。他们的孩子今年3岁,已经在上幼儿园。由于种种原因,林立一家并没有自己的房产,他们一直租住在朋友的一套公寓里。林立希望通过投资理财的方式,能使家庭的财…

绝大多数的理财投资app都会提供一个定投的功能,定投就是定期定额投资指定标的。如此推荐当然是因为他的优点很多,同时受理方也相对会获得更多的存量资金,算是双赢,只是周期较长,对于不懂理财的人来说可能就跟普通人去跑一万米一样长。
以基金为例,一般基金的交易平台除了申购/赎回的接口,也会提供定投的接口,只是调用第三方接口的话,一方面提高了耦合性,另一方面为了优雅地向用户展示定投相关信息,就需要付出额外的工作,因此我们需要DIY,然后按时调用买入的接口完成投资即可。

  林立在上海一家广告公司担任宣传策划,月收入5000元。他的妻子在一家咨询公司工作,月收入4000元。他们的孩子今年3岁,已经在上幼儿园。由于种种原因,林立一家并没有自己的房产,他们一直租住在朋友的一套公寓里。林立希望通过投资理财的方式,能使家庭的财富增加,并能实现购房、子女教育、养老这三大理财目标。

一、定投的数据模型

  • 首先,从定投的定义上来考虑:定期定额买入指定的投资标的
    1)我们需要知道是谁(user_id)
    2)定期,又分为按日、按周、按月(cycle_unit,jyrq)
    3)定额(apply_num)
    4 )
    投资标的,要考虑扩展性,可买的不仅仅是单个基金(invest_flag,invest_code)
    5)一般理财app都支持绑定多张卡,需要用户指定从哪张卡里扣款(trade_acoo)

  • 其次,要从定投执行过程方面来设计
    6)考虑到用户会出现资金紧张,可以允许顺延,但有最大天数限制,超出则判定失败,如果连续多次失败则认定用户已放弃定投,自动停止(delay_day,delay_count,fail_count)
    7)用户可能想知道下一次扣款会在哪天,同时考虑到顺延等状况,需要更新当前的扣款日期以便执行(next_kkdate,cur_kkdate)
    8 )累积的投资金额和成功次数(total_sum,count)
    9 )标记定投的状态,激活的or被终止了(is_active,soft_del)

  • 最后,还要考虑定投结果的展示
    9)定投结果要关联定投、标记结果状态–成功/失败(aip_id, state)
    10)如果成功还需要关联交易订单、交易金额、交易日期(order_id,apply_sum,trade_date)

综上,在models.py中定义如下

class Aip(Document):
    '''
    自动投资计划:Automatic investment plan
    '''
    meta = {'db_alias': 'test', 'indexes': [略]}
    user_id = IntField(required=True)
    invest_flag = StringField(required=True)
    invest_code = StringField(required=True)
    apply_sum = StringField(required=True)
    trade_acco = StringField(required=True)
    cycle_unit = StringField(required=True)
    jyrq = StringField(required=True)  
    delay_day = IntField(default=2)
    next_kkdate = DateTimeField()    # 考虑顺延和工作日
    cur_kkdate = DateTimeField()      # 不考虑顺延和工作日,用于连续循环更新
    total_sum = IntField(default=0)
    count = IntField(default=0)
    delay_count = IntField(default=0)
    fail_count = IntField(default=0)
    is_active = BooleanField(default=True)
    soft_del = BooleanField(default=False)
    created = DateTimeField(default=datetime.datetime.now)

    # 扣款日期描述
    @property
    def kkdate_desc(self):
        if self.cycle_unit == '0':
            desc = '每月' + str(int(self.jyrq)) + '日'
        elif self.cycle_unit == '1':
            desc = '每周' + WEEKDAY_DICT[int(self.jyrq)]
        elif self.cycle_unit == '2':
            desc = '每天'
        else:
            desc = ''
        return desc

class Aiphis(Document):
    meta = {'db_alias': 'test', 'indexes': [略)]}

    aip_id = StringField(required=True)
    state = StringField(required=True)
    order_id = StringField()           # 关联交易订单
    apply_sum = StringField()
    trade_date = DateTimeField()
    created = DateTimeField(default=datetime.datetime.now)

  广发基金理财师建议,采取基金定投方式帮助林立一家实现理财目标。基金定投是投资者和银行约定,每月在固定时间以固定金额,自动扣款到选定的开放式基金当中去,投资者可以将每月剩余收入参加基金定投,利用投资的复利来积累财富,以满足人生的各种理财目标。想要为家庭的不同成员打造相应的理财计划,需要“对症下药”,这也是为了让各个家庭成员能有适合自己的理财方式,并最终将财富升级。

二、用户的交互

与用户的交互当然是前端操作,但后端需要考虑到操作需求,以便提供足够丰富的接口,最基本的不外乎对于Aip的增删改查和对Aiphis的查,Aiphis的增是在执行中调用。
1)create_aip # 创建
2)query_aip_detail #
查询单个aip详情,调用get_aiphis_list获取对应的定投历史
3)query_aip_list # 查询用户名下所有的定投计划
4)update_aip #
更新、包括修改日期、金额、标的,暂停、激活、终止、重启
5)create_aiphis # 定投执行时,不论成败均会创建一条记录
6)get_aiphis_list # 获取定投历史记录

  计划一:购房计划

三、定投的执行

每日执行定投的任务脚本
1、只有扣款日期next_kkdate == today才会执行
2、定投成功,创建成功的Aiphis — 5
3、余额不足则顺延,

  • 1)日定投不顺眼直接判定失败,创建失败的Aiphis — 5
  • 2)顺延次数+1,达到次数限制则判定失败,aip的顺延次数清零,创建失败的Aiphis
  • 3)其余定投方式要将next_kkdate顺延至下一个交易日,并创建Aiphis

4、定投失败,同时要创建失败的Aiphis — 5
5、创建Aiphis

  • 1)失败,要更新失败次数 — 6
  • 2)成功,关联当前的order_id,更新aip的total_sum和count,并清空aip的顺延和失败次数
  • 3)不论成功失败,都需要更新下一次扣款日期 — 7

6、更新失失败次数,+1,达到上限则终止定投计划
8、更新下一次扣款日期(定投日期)

  • 1)首先根据cur_kkdate计算下一个周期的日期next_day(不一定是交易日)
  • 2)其次根据next_day寻找最近的一个交易日next_tradeday,包括next_day自身
  • 3)如果是按天定投,则cur_kkdate = next_kkdate =
    next_tradeday;否则,cur_kkdate = next_day,next_kkdate =
    next_tradeday。

9、考虑到会出现第三方买入接口超时导致结果不确定的情况,应当当时的order_id,创建Aiphis并标记为“未知”,然后第二天进行同步检查
— 0
0、
每天执行前,需要同步检查前一日标记为“未知”的定投记录,然后根据成功、失败、未知分别处理。

最后贴上update_next_kkdate的代码,并推荐一个处理时间间隔的第三方库dateutil.relativedelta,让你在处理时间的时候摆脱边界问题的困扰。

# 按照Pep8的要求分块引入内建、第三方、自己实现的库
import datetime

from dateutil.relativedelta import relativedelta

from util.date import TradeDay        # 自定义抓取的交易日期记录


def update_next_kkdate(aip):
    aip = aip if hasattr(aip, 'id') else Aip.get(id=aip)
    td = aip.cur_kkdate
    if aip.cycle_unit == '0':
        nd = td + relativedelta(months=1)      # 月
    elif aip.cycle_unit == '1':
        nd = td + relativedelta(weeks=1)        # 周
    elif aip.cycle_unit == '2':
        nd = td + relativedelta(days=1)          # 日
    else:
        nd = td
    # 最近的一个交易日
    tradeday = TradeDay.objects(
        trade_day__gte=int(nd.strftime("%Y%m%d"))).first()
    aip.next_kkdate = datetime.datetime.strptime(
        str(tradeday.trade_day), "%Y%m%d")

    if aip.cycle_unit == '2':
        aip.cur_kkdate = aip.next_kkdate
    else:
        aip.cur_kkdate = nd
    aip.save()

  林立和妻子希望能够尽快拥有自己的一套房子。理财师建议,可以采取基金定投的方式。具体做法是每月把薪金中的一部分,如林先生的工资一部分和妻子工资的一部分定投到成长性好的股票型基金,经过约3至5年的时间,经过本金积累和基金投资复利收益,基本能够支付首套住房的首期,为整个家庭奠定坚实的物质基础。此后,通过基金定投所获收益还能支付部分按揭款,可谓是一举两得。

  计划二:教育计划

  作为家庭的未来,如何让孩子拥有一个美好的将来?理财师认为,对于孩子而言,最重要的就是教育问题,教育决定未来,尽可能让孩子接受高等教育,这是广大父母的共识。但孩子的教育费用实在是一笔非常大的投资。很多家庭目前还在采取每月储蓄定额存款的方法为孩子积累日后的教育资本,显然已经不适合现代家庭理财。专家建议,让父母们用基金定投方式储蓄子女教育基金。

相关文章