基于Python的人工股票市场模拟

为了对证券所的交易制度和股票的价格的机理进行更加深刻的理解,在机缘巧合之下,我用python参考一篇论文【1】构建了一个模拟的人工股票市场,在这里给大家分享一下模型的搭建过程,以便于感兴趣的读者进行更深层次的研究。

1. 研究方法

随着复杂性科学和计算机模拟技术的发展,自20世纪90年代起,自上而下的多主体建模(基于主体的建模)方法开始逐步被引入金融领域,主要应用于金融市场的微观结构和交易策略的研究,并因其依靠数学建模和人工模拟的方法创新成为金融市场研究的热点。

本文拟基于多主体建模的方法,自下而上地构建贴近实际的人工股票市场,并且含有投资者的财富信息,以便研究股票市场中投资者的财富分布和演化,或者对整体财富分布进行基尼系数的测算。

2. 模型设计

为了简化模型的设计过程,假定市场中只有一支可以和现金交换的股票,股票在市场中的流通总数不变且无红利发放,现金的收益率为0(即不存在时间价值,无法利用其持有的资金进行其他投资)。

2.1 基本市场框架

中国证券交易采用计算机集合竞价和连续竞价两种方式,由于集合竞价在每日交易中占据的时间较短,并且参与主体也有所不同,所以只关注每日交易中的连续竞价过程,直接用前一天的收盘价作为当天开盘价的近似,并用最后一笔成交价格作为当日的收盘价格。

具体地,关注人工股票市场的T个交易日,在每个交易日,市场中的N个投资者按随机顺序进入,并根据自己对股票未来价格的预测提交订单。假定每个投资者每日有且仅有一次机会进入市场,这样以投资者的申报为间隔,每个交易日包含N个交易区间。市场根据投资者的申报按照价格优先与时间优先的顺序撮合成交。未成交的订单暂存在当日的指令簿中,在交易日结束时,仍未成交的订单会被清空。

2.2 交易参与主体——异质投资者

每日有N位投资者参与市场交易,与传统金融理论所假设的同质代理人不同,这里强调了三个异质主体,主要体现在根据自身的不同禀赋对股票未来价格有不同的预测及策略行为。

2.2.1 投资者的初始禀赋

投资者初始禀赋的不同在模型的初始设定时主要体现在两个方面,一是投资者的风险厌恶程度不同,以α(i)标识。它决定了投资者每次申购时拿出多少比例的现有可利用财富进行买卖报量,该比例假设服从均匀分布 α(i)~U[α1,α2]; 二是投资者对信息资源的获取与处理能力不同。大致分为3类:1) 能够获取较为准确的信息并有 较强处理能力的机构投资者,体现在他们既能够得到上市公司股票的较为准确的价值信息vt,又掌握通过以往市场信息推导价格变动趋势的技术手段。2) 仅能够利用股票价格的历史信息进行处理并推导未来价格变动趋势的 趋势交易者。现实中利用市场历史信息进行技术交易的种类繁多,交易策略也很丰富。这里仅采用最常见的趋势交易作为初步探讨。3)不以股市盈利为目标的噪声交易者,该类交易者往往忽视股票市场中的所有信息,他们参与市场是为了流动性或其它便利性的需求。 事实上,不同投资者进入市场时的财富也不尽相同,而不同的财富会使得投资者可以进行的交易种类(如有些交易会有资金的门槛)及摩擦费用(如佣金、税收等)有所差异,这自然会增加模型的维度及复杂性。为了简化,这里赋予所有投资者相同的财富(由现金与股票构成)。

2.2.2 投资者的策略行为

2.2.2.1投资者对未来价格的预测

依据投资者对信息资源的获取禀赋与处理能力的不同,分别阐述机构投资者、趋势交易者与噪声交易者3类不同投资者对未来价格的预测方式。 第1类是机构投资者。他们拥有最准确的信息资源———股票的价值vt,可以进行价值投资。该投资理念最早由格雷厄姆提出,其核心在于对企业价值的研究,认为股票价格会围绕其自身价值上下波动,即当股价低于(高)于价值时将会上涨(下跌)。据此,第i个机构投资者在第t日采取价值投资时对当日股票收盘价的预测pe 为式(1)所示:

式(1)

假设股票的价值vt服从几何随机游走过程:

ηt为服从正态分布的随机噪声
d(i)1标识不同机构投资者对价格回复到价值速度快慢的不同看法,假设其服从均匀分布
e(i)t标识机构投资者在获取价值信息时所受到的随机干扰,假设其服从

的正态分布。然而,现阶段中国股票市场还不够成熟,很多机构投资者也会捕捉某些明确的市场走势,顺势而为,采取趋势追随策略以获取短期收益。据此,该文认为机构投资者有时也会根据市场走势通过追随趋势预测价格,具体形式 如式(2)所示。

式(2)

类似地,

为机构投资者追随趋势的强烈程度(d(i)2>0)。
为第i个机构投资者所记录的前L(i)1期的平均移动价格,其中,L(i)1为机构投资者用来追踪价格趋势所选取的移动均线的不同时间尺度,假设其服从均匀分布:
而其误差为服从

的正态分布,反映机构投资者对自身分析的趋势信息的把握程度。需要注意的是,这里的方差为时变的方差,代表投资者对其关注的近期价格趋势在时间尺度L(i)1内的市场波动性的估计(如式(3)所示)。一般来说,当市场波动性大时,投资者对自身预测的把握程度会降低,其给出的价格预测的区间会更加宽泛。
式(3)

既然机构投资者在获取上市公司的价值信息和分析市场的价格趋势上都有能力与优势,那么具体到每日交 易,机构投资者选择哪种方式来形成价格预测呢?一个最直观的假设是看哪种预测方式给出的信号最为强烈或在近期表现得最好。具体地,采取式(4)的形式。


式(4)

机构投资者对预测形式的选择主要体现在g(i)t这个二值函数上,其具体形式为

其中:

以价值投资的角度衡量价格偏离价值的深度,它决定了价值投资的获利空间有多大;而Score(i)t则以技术分析的角度通过计算追随趋势的加权平均累积收益来记录其稳定获利的能力,如式(5):
式(5)

其中sign()为一符号函数,当前一日收盘价大于(小于)移动均线值时,趋势追随者预测该趋势仍会持续,给出买入(卖出)的信号sign = 1(sign = -1)。而该信号是否准确,要根据下一期真实实现的市场收益 rt = log(pt) - log(p(t-1))来判断。但是因为本期无法知道市场的收盘价,因此采用往期数据的得分累计值来作为当期分数的参考。当预测与真实价格同向变动时,该趋势追随的技术分析方法获得数量为市场收益的正的得分,表明其前一期的预测能够为投资者带来正的收益。而由于短期的趋势追随预测 易受到市场中随机噪声的影响,机构投资者往往关注于该技术分析方法获利的稳定性。所以本文采取加权平均的 累积收益来计分。其权重 0 < \delta < 1为一贴现因子,反映越近期的预测的准确程度对其得分函数的影响越大。当该累积得分Score(i)t > |ft| 时,表明此时的市场形势下追随趋势可能比价值投资更有获利性,故机构投资者会采取趋势追随来形成价格预测,即 g(i)t = 1 ,式(4)退化到式(2)的形式。反之,式(4)退化到式(3)的形式。

第2类是趋势交易者。他们无法获得较为准确的上市公司的价值信息(或成本太大),只能通过技术分析的手段依据近期价格趋势推测未来价格,其具体形式如式(6):
式(6)

类似的,d(i)3为趋势交易者利用趋势信息推测未来价格的强烈程度,其分布假设为均匀分布。与机构投资者的趋势追随策略不同的是,d(i)3 > 0 表示趋势追随,d(i)3 < 0 表示趋势反转(实际上,机构投资者的反转策略是通过价值投资来实现的)。移动平均价格、观测尺度、时变的方差等与第一类投资者采取的分布相同。

第3类是噪声交易者,他们为了满足自身流动性或风险对冲的需求进入市场,并不通过股票市场信息来盈利,其对价格的预测由当日的开盘价(前一日的收盘价)加上一个正态分布的随机噪声构成。
式(7)

2.2.2.2 投资者的申购行为

各类投资者根据自身的资源禀赋及信息处理能力形成价格预测后,便进入市场参与交易。由于目前中国证券交易主要由限价订单构成,所以本文只考虑限价订单,由报价和报量两部分构成。 对于报价,当投资者预测价格将会上涨(下跌)时,结合自己的预测及下单前观测到的市场即时信息来进行买入(卖出)报价,买(bid(i)t) 卖 (ask(i)t) 报价的具体形式分别见式(8),式(9):
式(8)
式(9)

其中,

为当日最近m次的平均成交价格(现价,我在模型中将m取1),买卖报价的基本原则是当投资者作为买(卖)方进入市场时,若观测到的最近几次成交的价格低(高)于自己对当日收盘价的预测,则提交一个介于近期成交平均价与自己预测价之间的价格以提高成交的概率;若观测到的最近几次成交的价格已经高(低)于自己对当日收盘价的预测,投资者也不会提交一个高(低)于自己预测的价格去买(卖),则会提交一个介于昨日收盘价与自己预测价格之间的价格等待成交。对于报量,投资者会根据自身的风险偏好程度 α(i) 与当前可利用的财富(不考虑买空卖空)来确定。这里采用最简洁直接的形式——买卖报量与投资者的风险偏好及财富成正比,即买入报量:

卖出报量:

其中 c(i)(t-1) 与 s(i)(t-1) 分别为投资者在上一期(当前可用)的现金财富与股票财富。

2.3 价格形成——连续双向拍卖机制

各类投资者按随机顺序进入市场下单,基于连续双向拍卖的基本机制,采用上海证券交易所的规则来确定双方的成交价格。具体地,进入市场的订单按价格优先和时间优先的准则在订单簿中排序,最优买入申报价格为最 高买入申报价格,记为B;最优卖出申报价格为最低卖出申报价格,记为 A。若新进入的买单报价>= A, 则以 A 的价格成交,即 q(现价)= A; 若新进入的卖单报价<=B,则以B的价格成交,即q(现价) = B。若最优买入申报价格与最优卖出申报价格相同,则q(现价)= A= B

当所有投资者都陆续进入市场完成一次申购后,一个交易日结束。各类投资者根据各自的成交情况对自身的现金财富和股票财富进行清算,见式(10)。
式(10)

其中,p(i)\tau 与 q(i)\tau 分别表示当日第i个投资者在\tau 时刻成交的成交价格与成交量。投资者每日的总财富为更新后的现金财富与以当日收盘价结算的股票财富之和,即

至此,模型封闭。随着时间的推进,模型进入下一期的循环。

3. 模拟结果

假设每类投资者都有100人,初始财富都为100股的股票+10000元,股票的初始价值为100元每股,且价值符合随机游走过程,模拟100天的结果如图所示:
运行100期价格图

当然,该模型中也包含了各类投资者的财富变化,对模型进行适当的修改,还可以考虑T+1制度、涨跌停等制度对财富分布、价格分布的影响。这里给出源码:

# -*- coding: utf-8 -*-
# @Time    : 2019/3/5 16:23
# @Author  : Arron Zhang
# @Email   : 549144697@qq.com
# @File    : Simulation of Stock Market.py
# @Software: PyCharm

import math
import matplotlib.pyplot as plt
from numpy import random
import numpy as np
import pandas as pd
from itertools import chain
from math import e, log
# 构造投资者类别,返回其对当日收盘价的预测


def inv_1(pclose,score, v):
    # 机构投资者价值预测噪声误差
    e2 = 0.5
    e2 = random.normal(loc=0.0, scale=e2)
    # 机构投资者预测价格回复价值速度
    d1 = random.uniform(0.2, 0.8)
    # 机构投资者预测价格追随趋势程度
    d2 = random.uniform(0.2, 0.8)
    # 计算N日的均值
    N = random.randint(2, 30)
    ma = np.sum(pclose[-(N+1): -1])/N
    vmean = 0
    for i in range(N):
        a = i+1
        vmean = (pclose[-(a+1)] - ma)**2 + vmean
    mean = vmean/N
    mean = mean**0.5
    e1 = random.normal(loc=0,scale=mean)
    # 贴现因子,越靠近当日对score的影响越大
    sigma = 0.9
    # 上期收益率
    r = math.log10(pclose[-1]) - math.log10(pclose[-2])
    sign = 0
    if r > 0:
        sign = 1
    elif r <= 0:
        sign = -1
    score = sign * (pclose[-2] - ma) * r + sigma * score
    f = math.log10(v[-2]) - math.log10(pclose[-2])
    g = 0
    if score > abs(f):
        g = 1
    elif score <= abs(f):
        g = 0
    pred = pclose[-2] + g*(d2*(pclose[-2] - ma)+e1) + (1 - g)*(d1*(v[-2] - pclose[-2]) + e2)
    if pred > 0:
        pass
    else:
        pred = 0

    return [pred, score]


def inv_2(pclose):
    # 趋势交易者预测价格持续或反转程度
    d3 = random.uniform(-1.5, 1.5)
    # 计算N日的均值
    if len(pclose) >= 30:
        N = random.randint(2, 31)
    else:
        N = random.randint(1, len(pclose) + 1)
    ma = np.sum(pclose[-N:]) / N
    vmean = 0
    for i in range(N):
        a = i + 1
        vmean = (pclose[-a] - ma) ** 2 + vmean
    mean = vmean / N
    mean = mean**0.5
    e4 = random.normal(loc=0, scale=mean)
    pred = pclose[-1] + d3*(pclose[-1] - ma) + e4
    if pred > 0:
        pass
    else:
        pred = 0
    return pred


def inv_3(pclose):
    # 噪声交易者预测的噪声标准差
    e5 = 0.5
    e5 = random.normal(loc=0, scale=e5)
    pred = pclose[-1] + e5
    if pred > 0:
        pass
    else:
        pred = 0
    return pred


# 三类交易者根据即时成交价格形成报价
def giveprice_1(pclose, pred, price):
    callprice = 0
    # 交易信号,1 为买入报价, -1 为卖出报价
    tradesign = 0
    if pred > pclose:
        tradesign = 1
        if pred > price:
            callprice = random.uniform(price,pred)
        else:
            callprice = random.uniform(pclose,pred)
    elif pred <= pclose:
        tradesign = -1
        if pred > price:
            callprice = random.uniform(pred, pclose)
        else:
            callprice = random.uniform(pred, price)

    if callprice > 0:
        pass
    else:
        giveprice_1(pclose, pred, price)

    return [callprice, tradesign]


# 生成初始的投资者信息
def generate_origin_info():
    #三类投资者数量 n_1,n_2,n_3
    n_1 = 100
    n_2 = 100
    n_3 = 100
    # 投资者分类列表
    list_1 = []
    # 投资者资金列表
    list_2 = []
    # 投资者持有的股票数量列表
    list_3 = []
    # 投资者拥有的财富列表
    list_4 = []
    # 投资者使用的方法得分
    list_5 = []
    for i in range(n_1):
        list_1.append(1)
    for i in range(n_2):
        list_1.append(2)
    for i in range(n_3):
        list_1.append(3)

    n = n_2 + n_1 + n_3
    for i in range(n):
        list_2.append(10000)
        list_3.append(100)
        list_4.append(20000)
        list_5.append(0)

    dictionary = {'type': list_1, 'money': list_2, 'volume': list_3, 'fortune': list_4, 'score': list_5}
    primeinfo = pd.DataFrame(dictionary)
    return primeinfo


# 执行一天的交易过程
def buy_sold_a_day(investorinfo, pclose, v):
    buy = [[len(investorinfo) + 1, 0, 0]]
    sold = [[len(investorinfo) + 1, 10000, 0]]
    price = []
    price.append(pclose[-1])
    # 按照随机顺序使得投资者进入市场
    rd = []
    for i in range(len(investorinfo)):
        rd.append(i)
    random.shuffle(rd)
    for i in range(len(rd)):
        candidate = investorinfo.iloc[rd[i]]
        # 第一类投资者
        if candidate[0] == 1:
            score = candidate[4]
            # 形成第一类投资者的报价,分数
            respond = inv_1(pclose, score, v)
            # 更新分数
            investorinfo.iloc[rd[i], 4] = respond[1]
            preclose = pclose[-1]
            price_now = price[-1]
            pred_1 = respond[0]
            callprice_1 = giveprice_1(preclose, pred_1, price_now)

            # 判断买入报价和卖出报价,1 为买入,-1为卖出
            if callprice_1[1] == 1:
                # 买入报量
                money_now = candidate[1]
                if money_now > 0:
                    afa = random.uniform(0.25, 0.85)
                    quant = int(afa * (money_now / callprice_1[0]))
                    q_origin = quant
                    # 最终成交量
                    q = 0
                    sold.sort(key=lambda x: x[1])
                    j = 0
                    # 将买入报价写入买单,在完成交易后更新成交量
                    buy.append([rd[i], callprice_1[0], quant])
                    # 寻找所有卖出报价低于买入报价的卖家报单,每完成一笔,更新持仓量
                    while sold[j][1] <= callprice_1[0]:
                        if sold[j][2] >= quant and quant> 0:
                            sold[j][2] = sold[j][2] - quant
                            q = q + quant
                            if quant > 0:
                                price.append(sold[j][1])
                                investorinfo.iloc[sold[j][0], 1] = investorinfo.copy().iloc[sold[j][0], 1] + quant*sold[j][1]
                                investorinfo.iloc[rd[i], 1] = investorinfo.copy().iloc[rd[i], 1] - quant * sold[j][1]
                                investorinfo.iloc[sold[j][0], 2] = investorinfo.copy().iloc[sold[j][0], 2] - quant
                                quant = 0
                            else:
                                pass
                        elif sold[j][2] < quant and sold[j][2] > 0:
                            quant = quant - sold[j][2]
                            q = q + sold[j][2]
                            if sold[j][2] > 0:
                                price.append(sold[j][1])
                                investorinfo.iloc[rd[i], 1] = investorinfo.copy().iloc[rd[i], 1] - sold[j][2] * sold[j][1]
                                investorinfo.iloc[sold[j][0], 1] = investorinfo.copy().iloc[sold[j][0], 1] + sold[j][2] * sold[j][1]
                                investorinfo.iloc[sold[j][0], 2] = investorinfo.copy().iloc[sold[j][0], 2] - sold[j][2]
                            sold[j][2] = 0
                        if j + 1 >= len(sold):
                            break
                        else:
                            j = j + 1
                    buy[-1][2] = q_origin - q
                    investorinfo.iloc[rd[i], 2] = investorinfo.copy().iloc[rd[i], 2] + q
                else:
                    pass

            elif callprice_1[1] == -1:
                if candidate[2] > 0:
                    afa = random.uniform(0.25, 0.85)
                    quant = int(afa * candidate[2])
                    q_origin = quant
                    # 最终成交量
                    q = 0
                    buy.sort(key=lambda x: x[1], reverse=True)
                    j = 0
                    # 将卖出报价写入卖价报单,在完成交易后更新成交量
                    sold.append([rd[i], callprice_1[0], quant])
                    # 寻找所有买入报价高于卖出报价的买家报单,每完成一笔,更新持仓量
                    while buy[j][1] >= callprice_1[0]:
                        if buy[j][2] >= quant and quant > 0:
                            buy[j][2] = buy[j][2] - quant
                            q = q + quant
                            if quant > 0:
                                price.append(buy[j][1])
                                investorinfo.iloc[buy[j][0], 1] = investorinfo.copy().iloc[buy[j][0], 1] - quant * buy[j][1]
                                investorinfo.iloc[rd[i], 1] = investorinfo.copy().iloc[rd[i], 1] + quant * buy[j][1]
                                investorinfo.iloc[buy[j][0], 2] = investorinfo.copy().iloc[buy[j][0], 2] + quant
                                quant = 0
                            else:
                                pass
                        elif buy[j][2] < quant and buy[j][2] > 0:
                            quant = quant - buy[j][2]
                            q = q + buy[j][2]
                            if buy[j][2] > 0:
                                price.append(buy[j][1])
                                investorinfo.iloc[buy[j][0], 1] = investorinfo.copy().iloc[buy[j][0], 1] - buy[j][2] * buy[j][1]
                                investorinfo.iloc[rd[i], 1] = investorinfo.copy().iloc[rd[i], 1] + buy[j][2] * buy[j][1]
                                investorinfo.iloc[buy[j][0], 2] = investorinfo.copy().iloc[buy[j][0], 2] + buy[j][2]
                            else:
                                pass
                            buy[j][2] = 0
                        if j + 1 >= len(buy):
                            break
                        else:
                            j = j + 1
                    sold[-1][2] = q_origin - q
                    investorinfo.iloc[rd[i], 2] = investorinfo.copy().iloc[rd[i], 2] - q
                else:
                    pass

        # 第二类投资者
        elif candidate[0] == 2:
            respond = inv_2(pclose)
            # 更新分数
            preclose = pclose[-1]
            price_now = price[-1]
            pred_1 = respond
            callprice_1 = giveprice_1(preclose, pred_1, price_now)

            # 判断买入报价和卖出报价,1 为买入,-1为卖出
            if callprice_1[1] == 1:
                # 买入报量
                money_now = candidate[1]
                if money_now > 0:
                    afa = random.uniform(0.25, 0.85)
                    quant = int(afa * money_now / callprice_1[0])
                    q_origin = quant
                    # 最终成交量
                    q = 0
                    sold.sort(key=lambda x: x[1])
                    j = 0
                    # 将买入报价写入买单,在完成交易后更新成交量
                    buy.append([rd[i], callprice_1[0], quant])
                    # 寻找所有卖出报价低于买入报价的卖家报单,每完成一笔,更新持仓量
                    while sold[j][1] <= callprice_1[0]:
                        if sold[j][2] >= quant and quant > 0:
                            sold[j][2] = sold[j][2] - quant
                            q = q + quant
                            if quant > 0:
                                price.append(sold[j][1])
                                investorinfo.iloc[sold[j][0], 1] = investorinfo.copy().iloc[sold[j][0], 1] + quant*sold[j][1]
                                investorinfo.iloc[rd[i], 1] = investorinfo.copy().iloc[rd[i], 1] - quant * sold[j][1]
                                investorinfo.iloc[sold[j][0], 2] = investorinfo.copy().iloc[sold[j][0], 2] - quant
                                quant = 0
                            else:
                                pass
                        elif sold[j][2] < quant and sold[j][2] > 0:
                            quant = quant - sold[j][2]
                            q = q + sold[j][2]
                            if sold[j][2] > 0:
                                price.append(sold[j][1])
                                investorinfo.iloc[rd[i], 1] = investorinfo.copy().iloc[rd[i], 1] - sold[j][2] * sold[j][1]
                                investorinfo.iloc[sold[j][0], 1] = investorinfo.copy().iloc[sold[j][0], 1] + sold[j][2] * sold[j][1]
                                investorinfo.iloc[sold[j][0], 2] = investorinfo.copy().iloc[sold[j][0], 2] - sold[j][2]
                            sold[j][2] = 0
                        if j + 1 >= len(sold):
                            break
                        else:
                            j = j + 1
                    buy[-1][2] = q_origin - q
                    investorinfo.iloc[rd[i], 2] = investorinfo.copy().iloc[rd[i], 2] + q
                else:
                    pass

            elif callprice_1[1] == -1:
                if candidate[2] > 0:
                    afa = random.uniform(0.25, 0.85)
                    quant = int(afa * candidate[2])
                    q_origin = quant
                    # 最终成交量
                    q = 0
                    buy.sort(key=lambda x: x[1], reverse=True)
                    j = 0
                    # 将卖出报价写入卖价报单,在完成交易后更新成交量
                    sold.append([rd[i], callprice_1[0], quant])
                    # 寻找所有买入报价高于卖出报价的买家报单,每完成一笔,更新持仓量
                    while buy[j][1] >= callprice_1[0]:
                        if buy[j][2] >= quant and quant > 0:
                            buy[j][2] = buy[j][2] - quant
                            q = q + quant
                            if quant > 0:
                                price.append(buy[j][1])
                                investorinfo.iloc[buy[j][0], 1] = investorinfo.copy().iloc[buy[j][0], 1] - quant * buy[j][1]
                                investorinfo.iloc[rd[i], 1] = investorinfo.copy().iloc[rd[i], 1] + quant * buy[j][1]
                                investorinfo.iloc[buy[j][0], 2] = investorinfo.copy().iloc[buy[j][0], 2] + quant
                                quant = 0
                            else:
                                pass
                        elif buy[j][2] < quant and buy[j][2] > 0:
                            quant = quant - buy[j][2]
                            q = q + buy[j][2]
                            if buy[j][2] > 0:
                                price.append(buy[j][1])
                                investorinfo.iloc[buy[j][0], 1] = investorinfo.copy().iloc[buy[j][0], 1] - buy[j][2] * buy[j][1]
                                investorinfo.iloc[rd[i], 1] = investorinfo.copy().iloc[rd[i], 1] + buy[j][2] * buy[j][1]
                                investorinfo.iloc[buy[j][0], 2] = investorinfo.copy().iloc[buy[j][0], 2] + buy[j][2]
                            else:
                                pass
                            buy[j][2] = 0
                        if j + 1 >= len(buy):
                            break
                        else:
                            j = j + 1
                    sold[-1][2] = q_origin - q
                    investorinfo.iloc[rd[i], 2] = investorinfo.copy().iloc[rd[i], 2] - q
                else:
                    pass

        # 第三类投资者
        elif candidate[0] == 3:
            respond = inv_3(pclose)
            preclose = pclose[-1]
            price_now = price[-1]
            pred_1 = respond
            callprice_1 = giveprice_1(preclose, pred_1, price_now)

            # 判断买入报价和卖出报价,1 为买入,-1为卖出
            if callprice_1[1] == 1:
                # 买入报量
                money_now = candidate[1]
                if money_now > 0:
                    afa = random.uniform(0.25, 0.85)
                    quant = int(afa * money_now / callprice_1[0])
                    q_origin = quant
                    # 最终成交量
                    q = 0
                    sold.sort(key=lambda x: x[1])
                    j = 0
                    # 将买入报价写入买单,在完成交易后更新成交量
                    buy.append([rd[i], callprice_1[0], quant])
                    # 寻找所有卖出报价低于买入报价的卖家报单,每完成一笔,更新持仓量
                    while sold[j][1] <= callprice_1[0]:
                        if sold[j][2] >= quant and quant > 0:
                            sold[j][2] = sold[j][2] - quant
                            q = q + quant
                            if quant > 0:
                                price.append(sold[j][1])
                                investorinfo.iloc[sold[j][0], 1] = investorinfo.copy().iloc[sold[j][0], 1] + quant*sold[j][1]
                                investorinfo.iloc[rd[i], 1] = investorinfo.copy().iloc[rd[i], 1] - quant * sold[j][1]
                                investorinfo.iloc[sold[j][0], 2] = investorinfo.copy().iloc[sold[j][0], 2] - quant
                                quant = 0
                            else:
                                pass
                        elif sold[j][2] < quant and sold[j][2] > 0:
                            quant = quant - sold[j][2]
                            q = q + sold[j][2]
                            if sold[j][2] > 0:
                                price.append(sold[j][1])
                                investorinfo.iloc[rd[i], 1] = investorinfo.copy().iloc[rd[i], 1] - sold[j][2] * sold[j][1]
                                investorinfo.iloc[sold[j][0], 1] = investorinfo.copy().iloc[sold[j][0], 1] + sold[j][2] * sold[j][1]
                                investorinfo.iloc[sold[j][0], 2] = investorinfo.copy().iloc[sold[j][0], 2] - sold[j][2]
                            sold[j][2] = 0
                        if j + 1 >= len(sold):
                            break
                        else:
                            j = j + 1
                    buy[-1][2] = q_origin - q
                    investorinfo.iloc[rd[i], 2] = investorinfo.copy().iloc[rd[i], 2] + q
                else:
                    pass

            elif callprice_1[1] == -1:
                if candidate[2] > 0:
                    afa = random.uniform(0.25, 0.85)
                    quant = int(afa * candidate[2])
                    q_origin = quant
                    # 最终成交量
                    q = 0
                    buy.sort(key=lambda x: x[1], reverse=True)
                    j = 0
                    # 将卖出报价写入卖价报单,在完成交易后更新成交量
                    sold.append([rd[i], callprice_1[0], quant])
                    # 寻找所有买入报价高于卖出报价的买家报单,每完成一笔,更新持仓量
                    while buy[j][1] >= callprice_1[0]:
                        if buy[j][2] >= quant and quant > 0:
                            buy[j][2] = buy[j][2] - quant
                            q = q + quant
                            if quant > 0:
                                price.append(buy[j][1])
                                investorinfo.iloc[buy[j][0], 1] = investorinfo.copy().iloc[buy[j][0], 1] - quant * buy[j][1]
                                investorinfo.iloc[rd[i], 1] = investorinfo.copy().iloc[rd[i], 1] + quant * buy[j][1]
                                investorinfo.iloc[buy[j][0], 2] = investorinfo.copy().iloc[buy[j][0], 2] + quant
                                quant = 0
                            else:
                                pass
                        elif buy[j][2] < quant and buy[j][2] > 0:
                            quant = quant - buy[j][2]
                            q = q + buy[j][2]
                            if buy[j][2] > 0:
                                price.append(buy[j][1])
                                investorinfo.iloc[buy[j][0], 1] = investorinfo.copy().iloc[buy[j][0], 1] - buy[j][2] * buy[j][1]
                                investorinfo.iloc[rd[i], 1] = investorinfo.copy().iloc[rd[i], 1] + buy[j][2] * buy[j][1]
                                investorinfo.iloc[buy[j][0], 2] = investorinfo.copy().iloc[buy[j][0], 2] + buy[j][2]
                            else:
                                pass
                            buy[j][2] = 0
                        if j + 1 >= len(buy):
                            break
                        else:
                            j = j + 1
                    sold[-1][2] = q_origin - q
                    investorinfo.iloc[rd[i], 2] = investorinfo.copy().iloc[rd[i], 2] - q
                else:
                    pass




    # 更新最终财富,收盘价
    rate = price[-1]/pclose[-1]
    pclose = price[-1]
    investorinfo['fortune'] = investorinfo.apply(lambda x: x['money'] + price[-1] * x['volume'], axis=1)

    return investorinfo, pclose, price[1:], rate


# 前100期取收盘价为100
pclose = [100 for i in range(100)]
# 价值的随机游走分布
v = [100]
# 价值随机游走标准差
e3 = 0.05
price = []
# 收益率
rate = []
investorinfo = generate_origin_info()
# 设定要运行的期数:
num = 100
for i in range(num):
    value = v[-1]
    # v为股票的价值,符合随机游走过程,是个随机变量
    n = random.normal(loc=0.0, scale=e3)
    value = e**(log(value)+n)
    v.append(value)
    back = buy_sold_a_day(investorinfo, pclose, v)
    investorinfo = back[0]
    pclose.append(back[1])
    price.append(back[2])
    rate.append(back[3])
    print('\r当前进度:{:.2f}%'.format((i+1) * 100 / num), end='')
print(investorinfo)
print(pclose)
print(rate)


price = list(chain.from_iterable(price))
print(price)
draw_pclose = pclose[99:]
x = np.linspace(0, num, 1)
plt.title('All Price that happened')
plt.xlabel('Times')
plt.ylabel('price')
plt.plot(price)
plt.show()

4. 参考文献

【1】高言, 李昭辉. 基于人工股票市场的财富分布及演化研究[J]. 复杂系统与复杂性科学, 2015(1):17-27.

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容