基于Python的人工股票市場模擬

為了對證券所的交易制度和股票的價格的機理進行更加深刻的理解,在機緣巧合之下,我用python參考一篇論文【1】構建了一個模擬的人工股票市場,在這里給大家分享一下模型的搭建過程,以便于感興趣的讀者進行更深層次的研究。

1. 研究方法

隨著復雜性科學和計算機模擬技術的發(fā)展,自20世紀90年代起,自上而下的多主體建模(基于主體的建模)方法開始逐步被引入金融領域,主要應用于金融市場的微觀結構和交易策略的研究,并因其依靠數(shù)學建模和人工模擬的方法創(chuàng)新成為金融市場研究的熱點。

本文擬基于多主體建模的方法,自下而上地構建貼近實際的人工股票市場,并且含有投資者的財富信息,以便研究股票市場中投資者的財富分布和演化,或者對整體財富分布進行基尼系數(shù)的測算。

2. 模型設計

為了簡化模型的設計過程,假定市場中只有一支可以和現(xiàn)金交換的股票,股票在市場中的流通總數(shù)不變且無紅利發(fā)放,現(xiàn)金的收益率為0(即不存在時間價值,無法利用其持有的資金進行其他投資)。

2.1 基本市場框架

中國證券交易采用計算機集合競價和連續(xù)競價兩種方式,由于集合競價在每日交易中占據(jù)的時間較短,并且參與主體也有所不同,所以只關注每日交易中的連續(xù)競價過程,直接用前一天的收盤價作為當天開盤價的近似,并用最后一筆成交價格作為當日的收盤價格。

具體地,關注人工股票市場的T個交易日,在每個交易日,市場中的N個投資者按隨機順序進入,并根據(jù)自己對股票未來價格的預測提交訂單。假定每個投資者每日有且僅有一次機會進入市場,這樣以投資者的申報為間隔,每個交易日包含N個交易區(qū)間。市場根據(jù)投資者的申報按照價格優(yōu)先與時間優(yōu)先的順序撮合成交。未成交的訂單暫存在當日的指令簿中,在交易日結束時,仍未成交的訂單會被清空。

2.2 交易參與主體——異質投資者

每日有N位投資者參與市場交易,與傳統(tǒng)金融理論所假設的同質代理人不同,這里強調了三個異質主體,主要體現(xiàn)在根據(jù)自身的不同稟賦對股票未來價格有不同的預測及策略行為。

2.2.1 投資者的初始稟賦

投資者初始稟賦的不同在模型的初始設定時主要體現(xiàn)在兩個方面,一是投資者的風險厭惡程度不同,以α(i)標識。它決定了投資者每次申購時拿出多少比例的現(xiàn)有可利用財富進行買賣報量,該比例假設服從均勻分布 α(i)~U[α1,α2]; 二是投資者對信息資源的獲取與處理能力不同。大致分為3類:1) 能夠獲取較為準確的信息并有 較強處理能力的機構投資者,體現(xiàn)在他們既能夠得到上市公司股票的較為準確的價值信息vt,又掌握通過以往市場信息推導價格變動趨勢的技術手段。2) 僅能夠利用股票價格的歷史信息進行處理并推導未來價格變動趨勢的 趨勢交易者。現(xiàn)實中利用市場歷史信息進行技術交易的種類繁多,交易策略也很豐富。這里僅采用最常見的趨勢交易作為初步探討。3)不以股市盈利為目標的噪聲交易者,該類交易者往往忽視股票市場中的所有信息,他們參與市場是為了流動性或其它便利性的需求。 事實上,不同投資者進入市場時的財富也不盡相同,而不同的財富會使得投資者可以進行的交易種類(如有些交易會有資金的門檻)及摩擦費用(如傭金、稅收等)有所差異,這自然會增加模型的維度及復雜性。為了簡化,這里賦予所有投資者相同的財富(由現(xiàn)金與股票構成)。

2.2.2 投資者的策略行為

2.2.2.1投資者對未來價格的預測

依據(jù)投資者對信息資源的獲取稟賦與處理能力的不同,分別闡述機構投資者、趨勢交易者與噪聲交易者3類不同投資者對未來價格的預測方式。 第1類是機構投資者。他們擁有最準確的信息資源———股票的價值vt,可以進行價值投資。該投資理念最早由格雷厄姆提出,其核心在于對企業(yè)價值的研究,認為股票價格會圍繞其自身價值上下波動,即當股價低于(高)于價值時將會上漲(下跌)。據(jù)此,第i個機構投資者在第t日采取價值投資時對當日股票收盤價的預測pe 為式(1)所示:

式(1)

假設股票的價值vt服從幾何隨機游走過程:

ηt為服從正態(tài)分布的隨機噪聲
d(i)1標識不同機構投資者對價格回復到價值速度快慢的不同看法,假設其服從均勻分布
e(i)t標識機構投資者在獲取價值信息時所受到的隨機干擾,假設其服從

的正態(tài)分布。然而,現(xiàn)階段中國股票市場還不夠成熟,很多機構投資者也會捕捉某些明確的市場走勢,順勢而為,采取趨勢追隨策略以獲取短期收益。據(jù)此,該文認為機構投資者有時也會根據(jù)市場走勢通過追隨趨勢預測價格,具體形式 如式(2)所示。

式(2)

類似地,

為機構投資者追隨趨勢的強烈程度(d(i)2>0)。
為第i個機構投資者所記錄的前L(i)1期的平均移動價格,其中,L(i)1為機構投資者用來追蹤價格趨勢所選取的移動均線的不同時間尺度,假設其服從均勻分布:
而其誤差為服從

的正態(tài)分布,反映機構投資者對自身分析的趨勢信息的把握程度。需要注意的是,這里的方差為時變的方差,代表投資者對其關注的近期價格趨勢在時間尺度L(i)1內的市場波動性的估計(如式(3)所示)。一般來說,當市場波動性大時,投資者對自身預測的把握程度會降低,其給出的價格預測的區(qū)間會更加寬泛。
式(3)

既然機構投資者在獲取上市公司的價值信息和分析市場的價格趨勢上都有能力與優(yōu)勢,那么具體到每日交 易,機構投資者選擇哪種方式來形成價格預測呢?一個最直觀的假設是看哪種預測方式給出的信號最為強烈或在近期表現(xiàn)得最好。具體地,采取式(4)的形式。


式(4)

機構投資者對預測形式的選擇主要體現(xiàn)在g(i)t這個二值函數(shù)上,其具體形式為

其中:

以價值投資的角度衡量價格偏離價值的深度,它決定了價值投資的獲利空間有多大;而Score(i)t則以技術分析的角度通過計算追隨趨勢的加權平均累積收益來記錄其穩(wěn)定獲利的能力,如式(5):
式(5)

其中sign()為一符號函數(shù),當前一日收盤價大于(小于)移動均線值時,趨勢追隨者預測該趨勢仍會持續(xù),給出買入(賣出)的信號sign = 1(sign = -1)。而該信號是否準確,要根據(jù)下一期真實實現(xiàn)的市場收益 rt = log(pt) - log(p(t-1))來判斷。但是因為本期無法知道市場的收盤價,因此采用往期數(shù)據(jù)的得分累計值來作為當期分數(shù)的參考。當預測與真實價格同向變動時,該趨勢追隨的技術分析方法獲得數(shù)量為市場收益的正的得分,表明其前一期的預測能夠為投資者帶來正的收益。而由于短期的趨勢追隨預測 易受到市場中隨機噪聲的影響,機構投資者往往關注于該技術分析方法獲利的穩(wěn)定性。所以本文采取加權平均的 累積收益來計分。其權重 0 < \delta < 1為一貼現(xiàn)因子,反映越近期的預測的準確程度對其得分函數(shù)的影響越大。當該累積得分Score(i)t > |ft| 時,表明此時的市場形勢下追隨趨勢可能比價值投資更有獲利性,故機構投資者會采取趨勢追隨來形成價格預測,即 g(i)t = 1 ,式(4)退化到式(2)的形式。反之,式(4)退化到式(3)的形式。

第2類是趨勢交易者。他們無法獲得較為準確的上市公司的價值信息(或成本太大),只能通過技術分析的手段依據(jù)近期價格趨勢推測未來價格,其具體形式如式(6):
式(6)

類似的,d(i)3為趨勢交易者利用趨勢信息推測未來價格的強烈程度,其分布假設為均勻分布。與機構投資者的趨勢追隨策略不同的是,d(i)3 > 0 表示趨勢追隨,d(i)3 < 0 表示趨勢反轉(實際上,機構投資者的反轉策略是通過價值投資來實現(xiàn)的)。移動平均價格、觀測尺度、時變的方差等與第一類投資者采取的分布相同。

第3類是噪聲交易者,他們?yōu)榱藵M足自身流動性或風險對沖的需求進入市場,并不通過股票市場信息來盈利,其對價格的預測由當日的開盤價(前一日的收盤價)加上一個正態(tài)分布的隨機噪聲構成。
式(7)

2.2.2.2 投資者的申購行為

各類投資者根據(jù)自身的資源稟賦及信息處理能力形成價格預測后,便進入市場參與交易。由于目前中國證券交易主要由限價訂單構成,所以本文只考慮限價訂單,由報價和報量兩部分構成。 對于報價,當投資者預測價格將會上漲(下跌)時,結合自己的預測及下單前觀測到的市場即時信息來進行買入(賣出)報價,買(bid(i)t) 賣 (ask(i)t) 報價的具體形式分別見式(8),式(9):
式(8)
式(9)

其中,

為當日最近m次的平均成交價格(現(xiàn)價,我在模型中將m取1),買賣報價的基本原則是當投資者作為買(賣)方進入市場時,若觀測到的最近幾次成交的價格低(高)于自己對當日收盤價的預測,則提交一個介于近期成交平均價與自己預測價之間的價格以提高成交的概率;若觀測到的最近幾次成交的價格已經(jīng)高(低)于自己對當日收盤價的預測,投資者也不會提交一個高(低)于自己預測的價格去買(賣),則會提交一個介于昨日收盤價與自己預測價格之間的價格等待成交。對于報量,投資者會根據(jù)自身的風險偏好程度 α(i) 與當前可利用的財富(不考慮買空賣空)來確定。這里采用最簡潔直接的形式——買賣報量與投資者的風險偏好及財富成正比,即買入報量:

賣出報量:

其中 c(i)(t-1) 與 s(i)(t-1) 分別為投資者在上一期(當前可用)的現(xiàn)金財富與股票財富。

2.3 價格形成——連續(xù)雙向拍賣機制

各類投資者按隨機順序進入市場下單,基于連續(xù)雙向拍賣的基本機制,采用上海證券交易所的規(guī)則來確定雙方的成交價格。具體地,進入市場的訂單按價格優(yōu)先和時間優(yōu)先的準則在訂單簿中排序,最優(yōu)買入申報價格為最 高買入申報價格,記為B;最優(yōu)賣出申報價格為最低賣出申報價格,記為 A。若新進入的買單報價>= A, 則以 A 的價格成交,即 q(現(xiàn)價)= A; 若新進入的賣單報價<=B,則以B的價格成交,即q(現(xiàn)價) = B。若最優(yōu)買入申報價格與最優(yōu)賣出申報價格相同,則q(現(xiàn)價)= A= B。

當所有投資者都陸續(xù)進入市場完成一次申購后,一個交易日結束。各類投資者根據(jù)各自的成交情況對自身的現(xiàn)金財富和股票財富進行清算,見式(10)。
式(10)

其中,p(i)\tau 與 q(i)\tau 分別表示當日第i個投資者在\tau 時刻成交的成交價格與成交量。投資者每日的總財富為更新后的現(xiàn)金財富與以當日收盤價結算的股票財富之和,即

至此,模型封閉。隨著時間的推進,模型進入下一期的循環(huán)。

3. 模擬結果

假設每類投資者都有100人,初始財富都為100股的股票+10000元,股票的初始價值為100元每股,且價值符合隨機游走過程,模擬100天的結果如圖所示:
運行100期價格圖

當然,該模型中也包含了各類投資者的財富變化,對模型進行適當?shù)男薷模€可以考慮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)
    # 貼現(xiàn)因子,越靠近當日對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):
    # 趨勢交易者預測價格持續(xù)或反轉程度
    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


# 三類交易者根據(jù)即時成交價格形成報價
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():
    #三類投資者數(shù)量 n_1,n_2,n_3
    n_1 = 100
    n_2 = 100
    n_3 = 100
    # 投資者分類列表
    list_1 = []
    # 投資者資金列表
    list_2 = []
    # 投資者持有的股票數(shù)量列表
    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


# 執(zhí)行一天的交易過程
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]
            # 形成第一類投資者的報價,分數(shù)
            respond = inv_1(pclose, score, v)
            # 更新分數(shù)
            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)
            # 更新分數(shù)
            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()
# 設定要運行的期數(shù):
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]. 復雜系統(tǒng)與復雜性科學, 2015(1):17-27.

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容