属于房地产研究的一部分,这个研究希望能模拟地产公司、地方政府、居民和金融部门的互动关系,用面向对象的方法制作一个地产公司类以模拟地产企业的重要行为。
一、总体思考
会计报表能够反映企业的经营信息,因此我们打算使用一些会计科目来帮助我们设计房地产企业类(当然这些科目不是那么严格地对应于实际使用的会计科目的)。 我们主要使用资产负债表和利润表,不使用现金流量表(因为我们假设所有的交易都是使用现金交易的,即收付实现制)。
考虑一个典型的地产开发企业的业务就是从政府部门获取土地形成土地储备,使用土地储备生产商品房(库存商品),向居民销售商品房获取收入。其负债部分包括两部分:(1)向金融部门借入的有息负债(2)无息负债:主要是对供应商的应付账款和对居民的合同负债。
以下是简化的资产负债表和利润表图示:
二、属性与方法
依据这个框架,我们来设计地产公司类的主要属性。
名称 | 说明 |
name | 公司编号 |
endure | 存续状态,默认为True,根据某些标准可以判为破产 |
risk_preference | 风险偏好,即公司的冒险心态,分为HIGH/MIDDLE/LOW三个等级 |
assets | 总资产 |
currency_asset | 银行存款 |
land_reserve_num | 土地储备数量,单位是平方米 |
land_reserve_price | 土地储备的平均价格,由历史成本法计算移动平均数 |
land_reserve_value | 土地储备的总价值 |
store_num | 库存商品数量,单位是平方米 |
store_price | 库存商品的平均价格 |
store_value | 库存商品的总价值 |
debt_pool | 由字典组成的列表,每个字典都是一条目前存续的负债信息,包括三个字段:(1)debt_exist_year:债务剩余年限 (2)debt_num:债务本金 (3)debt_rate:债务利率 |
bank_debt | 有息负债总额 |
other_debt | 无息负债总额 |
total_debt | 总负债 |
reserve_profit | 留存收益 |
company_income | 销售收入 |
construction_cost | 为生产库存商品付出的建设成本,不包括土地储备转来的成本而和土地储备成本成一定比例 |
store_cost | 库存商品结转的成本 |
other_cost | 其他成本,一般指的是销售费用、管理费用等,与销售收入成一定比例 |
interst_cost | 利息费用 |
profit | 净利润 |
init_construction_rate | 初始的建设速率,是土地储备转库存商品的比例,可以在经营过程中调整(即企业可以决定本期将多少的土地储备投入生产) |
construction_cost_ration | 相比于土地储备成本的建设成本支出(人工、物料等) |
other_cost_ratio | 相比于销售收入的其他成本支出 |
plot_ratio | 容积率,按照这个比率将土地储备数量转为库存商品数量,例如一平米的土地储备可以生产三平米的商品房 |
init_bank_vs_other | 初始化的新增债务在有息负债和无息负债之间的比例分配,可以在运营过程中调整 |
deviation_rate | 与风险偏好程度决定的偏离率 |
expected_income_growth_rate_increse | 由财务风险水平对预期收入增长率的调整力度 |
rank | 按照收入计算的本公司在所有公司中的排名情况 |
doa | 资产负债率 |
roi | 净利率 |
ebit | 利息保障倍数 |
inv_tur_rate | 存货周转率 |
avg_debt_rate | 加权平均借款利率,反映公司的融资成本 |
is_balanced | 检查财务报表是否平衡 |
公司类进行初始化的时候是依据一个给定的总资产数量以及一系列的比例进行初始化的,此外还需要一些其他的参数,这些参数中不变的部分放入parameter_pool中,会变动的部分放入total_info中。
接下来我们考虑地产公司的主要行为/方法:
名称 | 说明 |
generate_land_quotation | 产生对政府部门的土地报价(包括价格和数量),影响因素是本年市场景气指数、公司风险偏好、上一年度排名、上一年度ebit、库存商品量和土地储备量。公司需要预估今年的销售量和库存以确定土地的意愿购买量,公司风险偏好越高,则给出的意愿价格就越高。 |
update_sheet_by_land | 接受由土地交易函数(即专栏中的交易系统设计部分)返回的土地订单信息,据此更新财务信息 |
update_sheet_by_produce | 由于使用土地储备生产库存商品导致的财务信息变动 |
generate_rs_quotation | 生成对居民部门的商品房报价,包括意愿提供的数量和价格 |
update_sheet_by_sale | 接受来自商品房交易函数返回的销售订单信息,据此更新财务信息 |
update_sheet_by_debt | 支付债务的本息,假设是期间支付利息,最后一期支付本金和利息 |
increase_debt | 检查货币资金是否为负数,如果是,则新增债务,并在有息负债和无息赌债之间分配 |
profit_carried_forward | 结转利润并判断账目是否平衡 |
update_finance | 更新doa/ebit/roi/inv_tur_rate信息,并判断企业是否破产 |
三、代码实现
class Company():
def __init__(self,company_dict):
#需要从company_dict传递过来的只需要name/总资产/风险偏好,其他项目都按照总资产的相应比例自行构造
self.endure = True
self.name = company_dict['name']
self.assets = company_dict['assets']
self.risk_preference = company_dict['risk_preference']
principle=self.assets*parameter_pool['init_bank_debt_ratio']
self.debt_pool = [
{
'debt_exist_year':parameter_pool['loan_year'],
'debt_num':principle,
'debt_rate':parameter_pool['init_company_loan_rate']
}
]
#------------主要资产的分配------------
self.currency_asset = self.assets*parameter_pool['init_currency_asset_ratio']
self.land_reserve_value = self.assets*parameter_pool['init_land_reserve_value_ratio']
self.land_reserve_price = parameter_pool['init_land_reserve_price']
self.land_reserve_num = self.land_reserve_value/self.land_reserve_price
self.store_value = self.assets*parameter_pool['init_store_value_ratio']
self.store_price = parameter_pool['init_store_price']
self.store_num = self.store_value/self.store_price
#-----------负债和所有者权益的分配----------------
self.bank_debt = self.assets*parameter_pool['init_bank_debt_ratio']
self.other_debt = self.assets*parameter_pool['init_other_debt_ratio']
self.total_debt = self.bank_debt + self.other_debt
self.reserve_profit = self.assets*parameter_pool['init_reserve_profit_ratio']
#------------利润表相关项目全部可以置为0-------------
# 上一年度期末利润表
self.company_income = self.assets*parameter_pool['init_company_income_ratio'] #主营业务收入
self.construction_cost = 0 #中转性质的科目,置为0
self.store_cost = self.assets*parameter_pool['init_store_cost_ratio'] #存货结转的成本
self.other_cost = self.assets*parameter_pool['init_other_cost_ratio']#其他的成本
self.interst_cost = self.assets*parameter_pool['init_interst_cost_ratio'] #利息支出
self.profit = self.company_income-self.store_cost-self.other_cost-self.interst_cost #本年利润
self.rank = total_info['reserved_company_income_ls'].index(self.company_income)+1
# # 上一年度现金流量表
# self.operating_in = 0 #经营活动现金收入,主来自产品销售
# self.operating_out = 0 #经营活动现金流出,主要是购置土地和生产库存商品中的现金支出
# self.financing_in = 0 #融资活动现金流入,来自银行负债和其他负债
# self.financing_out = 0 #融资活动现金支出,主要是支付负债利息
#财务信息汇总
self.doa = self.total_debt/self.assets #资产负债率
self.ebit = (self.profit+self.interst_cost)/self.interst_cost #利息保障倍数
self.roi = self.profit/self.company_income #净利率
self.inv_tur_rate = self.company_income/self.store_value #存货周转率,收入/存货
self.is_banlanced = (self.assets-self.total_debt-self.reserve_profit)<5
self.avg_debt_rate = parameter_pool['init_company_loan_rate'] #平均借款利率
#做一个记录历史信息的表格
self.history = pd.DataFrame(columns=[
'is_balanced',
'land_reserve_num',
'land_reserve_price',
'land_reserve_value',
'currency_asset',
'store_num',
'store_price',
'store_value',
'bank_debt',
'other_debt',
'reserve_profit',
'company_income',
'construction_cost',
'other_cost',
'store_cost',
'interst_cost',
'profit',
'avg_debt_rate',
'assets',
'debt',
'doa'
])
#根据本年市场景气指数、风险偏好、上一年度排名、上一年度ebit
#因为需要用到上一年度收入信息,因此,这个函数的调用在获得实际收入之前
def generate_land_quotation(self):
#计算按照上一年度收入的排名状况
rank = total_info['reserved_company_income_ls'].index(self.company_income)+1
#将排名映射为动力,要求最低的排名的动力增长为50%,最高的排名为0,共有100家公司
expansion_motivation = rank**0.85/100
expected_income_growth_rate = 0
#按照财务状况对景气指数进行更改
if self.ebit>= 1.5 and self.ebit<2.5: #财务状况正常
expected_income_growth_rate = total_info['prosperity_index_price']
elif self.ebit>=2.5: #财务状况良好
expected_income_growth_rate = total_info['prosperity_index_price'] + parameter_pool['expected_income_growth_rate_increse']
elif self.ebit < 1.5: #财务状况不良
expected_income_growth_rate = total_info['prosperity_index_price'] - parameter_pool['expected_income_growth_rate_increse']
#根据排名扩张动力、预期收入增长率确定预期销售收入
expected_income = self.company_income*(1+expansion_motivation)*(1+expected_income_growth_rate)
#计算预期销售量,由预期销售收入、市场景气指数、上期地产平均价格决定
expected_sale_num = expected_income/(total_info['last_rs_price']*(1+total_info['prosperity_index_price']))
#预期销售量-库存-可生产的潜力得到需要购买的土地量,不能小于0
land_willing_mount = max((expected_sale_num-self.store_num-self.land_reserve_num*parameter_pool['plot_ratio'])/parameter_pool['plot_ratio'],0)#如果小于0,则取0
#土地价格根据房产价格预期和风险倾向沟通决定
deviation_rate = 0
if self.risk_preference == 'HIGH':
deviation_rate = parameter_pool['deviation_rate']
else:
deviation_rate = -parameter_pool['deviation_rate']
#风险倾向越高,则喜欢出高价买地
land_willing_price = total_info['last_land_price']*(1+total_info['prosperity_index_price'])*(1+deviation_rate)
return {'name':self.name,'land_willing_price':land_willing_price,'land_willing_mount':land_willing_mount}
#由于土地购买导致的财务变动
def update_sheet_by_land(self,land_info):
#land_info是个字典组成的列表,包括了需求方名字、土地购买的量和价格,可能存在多笔土地成交记录,也可能啥也没有
try:
for info in land_info:
land_cost = info['land_num']*info['land_price']
self.land_reserve_num = self.land_reserve_num + info['land_num'] #新增土地储备数量
self.land_reserve_value = self.land_reserve_value + land_cost #新增土地储备价值
self.land_reserve_price = self.land_reserve_value/self.land_reserve_num #更新土地储备平均成本
self.currency_asset = self.currency_asset - land_cost #支付银行存款
except Exception:
print(str(self.name)+"号企业没有土地订单")
#由于实际生产导致的财务信息变动
def update_sheet_by_produce(self):
##1.借:库存商品 贷:土地储备 土地储备按照construction_rate转为库存商品
#construction_rate按照预期的收入增长率在init_construction_rate基础上改变
construction_num = self.land_reserve_num*parameter_pool['init_construction_rate']*(1+total_info['prosperity_index_price']) #本年度转为库存商品的土地储备数量
construction_value = construction_num*self.land_reserve_price #本年度土地储备转库存商品的值
self.land_reserve_num = self.land_reserve_num - construction_num #更新土地储备量
self.land_reserve_value = self.land_reserve_num*self.land_reserve_price #更新土地储备价值
self.store_num = self.store_num + construction_num*parameter_pool['plot_ratio'] #增加库存商品量,仅包括土地储备转来的那一部分
self.store_value = self.store_value + construction_value# 增加库存商品价值,仅包括土地储备转来的部分
##2.借:建设成本 贷:银行存款
construction_cost = construction_value*parameter_pool['construction_cost_ration']
self.construction_cost = self.construction_cost+construction_cost
self.currency_asset = self.currency_asset-construction_cost
##3.借:库存商品 贷:建设成本
self.construction_cost = self.construction_cost-construction_cost
self.store_value = self.store_value+construction_cost
self.store_price = self.store_value/self.store_num #更新库存商品价格
##4.其他成本由收入比例确定,在最后结转成本的时候记录
#进行地产市场报价
def generate_rs_quotation(self):
deviation_rate=0
if self.risk_preference=='HIGH':
deviation_rate=parameter_pool['deviation_rate']
elif self.risk_preference=='LOW':
deviation_rate=-parameter_pool['deviation_rate']
#计算按照上一年度收入的排名状况
rank = total_info['reserved_company_income_ls'].index(self.company_income)+1
#将排名映射为动力,要求最低的排名的动力增长为50%,最高的排名为0,共有100家公司
expansion_motivation = rank**0.85/100
expected_income_growth_rate = 0
#按照财务状况对景气指数进行更改
if self.ebit>= 0.5 and self.ebit<1: #财务状况正常
expected_income_growth_rate = total_info['prosperity_index_price']
elif self.ebit>=1: #财务状况良好
expected_income_growth_rate = total_info['prosperity_index_price'] + parameter_pool['expected_income_growth_rate_increse']
elif self.ebit < 0.5: #财务状况不良
expected_income_growth_rate = total_info['prosperity_index_price'] - parameter_pool['expected_income_growth_rate_increse']
#根据排名扩张动力、预期收入增长率确定预期销售收入
expected_income = self.company_income*(1+expansion_motivation)*(1+expected_income_growth_rate)
#计算预期销售量,由预期销售收入、市场景气指数、上期地产平均价格决定
expected_sale_num = expected_income/(total_info['last_rs_price']*(1+total_info['prosperity_index_price']))
rs_willing_price = total_info['last_rs_price']*(1+total_info['prosperity_index_price'])/(1+deviation_rate)
#意愿的销售量不能超过实际库存
rs_willing_num = max(expected_sale_num,self.store_num)
return {'name':self.name,'rs_willing_price':rs_willing_price,'rs_willing_num':rs_willing_num}
#根据地产撮合系统返回的信息获取收入、结转成本,sale_info是包括字典的列表
def update_sheet_by_sale(self,sale_info):
self.company_income = 0 #存在多笔销售,那么就需要累计了
self.store_cost = 0
self.other_cost = 0
try:
for info in sale_info:
#允许出现负数
##1.销售商品 借:银行存款 贷:主营业务收入
income = info['sell_num']*info['sell_price']
self.currency_asset = self.currency_asset + income
self.company_income =self.company_income + income
##2.借:存货成本 贷:库存商品
store_cost = info['sell_num']*self.store_price
self.store_cost =self.store_cost+ store_cost #借:存货结转成本
self.store_num = self.store_num - info['sell_num'] #更新库存数量
self.store_value = self.store_num*self.store_price #更新库存商品价值
##3.借:其他成本 贷:银行存款
other_cost = income*parameter_pool['other_cost_ratio']
self.other_cost = self.other_cost + other_cost
self.currency_asset = self.currency_asset - other_cost
except Exception:
print(str(self.name)+'缺少销售订单信息')
#如果库存被击穿,就通过银行存款购买库存商品,将其补充为0
if self.store_num < 0:
self.currency_asset -= -self.store_num*self.store_price
self.store_num = 0
#支付利息
def update_sheet_by_debt(self):
## 支付利息和本金,这是年末发生的事情
interst_cost = 0
principle_cost = 0 #本金支出
total_debt_num = 0
debt_num_list = []
debt_rate_list = []
try:
for single_debt_info in self.debt_pool:
single_interst = single_debt_info['debt_num']*single_debt_info['debt_rate']
total_debt_num = total_debt_num + single_debt_info['debt_num']
debt_num_list.append(single_debt_info['debt_num'])
debt_rate_list.append(single_debt_info['debt_rate'])
interst_cost += single_interst #计算总利息支出
single_debt_info['debt_exist_year'] -= 1 #削减贷款年限
if single_debt_info['debt_exist_year']==0: #最后一年的话要归还本金
principle_cost += single_debt_info['debt_num']
self.debt_pool.remove(single_debt_info) #从贷款信息池子里面删除这条信息
##记录对报表的影响
self.bank_debt = self.bank_debt - principle_cost #归还本金减少负债
self.currency_asset = self.currency_asset - principle_cost - interst_cost
self.interst_cost =interst_cost
#更新公司平均借款利率,按照权重计算
debt_num_array = np.array(debt_num_list)/total_debt_num
debt_rate_array = np.array(debt_rate_list)
self.avg_debt_rate = sum(debt_num_array*debt_rate_array)
except Exception:
print(str(self.name)+'没有负债!')
#根据银行存款余额确定债务的增加额度,如果银行存款为负数,则新增债务,并按照比值在有息债务和无息债务之间分配
#这个比例和有息负债成本(平均借款利率成反比)
def increase_debt(self):
if self.currency_asset<0:
num = -self.currency_asset
##借:银行存款 贷:银行负债
bank_debt = num*parameter_pool['init_bank_vs_other']*(1-self.avg_debt_rate)
self.currency_asset = self.currency_asset + bank_debt
self.bank_debt = self.bank_debt + bank_debt
##借:银行存款 贷:其他负债
other_debt = num*(1-parameter_pool['init_bank_vs_other']*(1-self.avg_debt_rate))
self.currency_asset = self.currency_asset + other_debt
self.other_debt = self.other_debt + other_debt
## 更新贷款池子信息
new_debt = {
'debt_exist_year':parameter_pool['loan_year'],
'debt_num':num,
'debt_rate':total_info['company_bank_debt_rate']
}
self.debt_pool.append(new_debt)
#结转利润,并判断账务是否平衡
def profit_carried_forward(self):
# 进行利润结转,假设盈余或者亏损全部进入留存收益项目
profit_num = self.company_income - self.store_cost-self.other_cost - self.interst_cost
self.profit = profit_num
self.reserve_profit = self.reserve_profit + profit_num
self.total_debt = self.bank_debt + self.other_debt
self.assets = self.land_reserve_value + self.store_value + self.currency_asset
self.is_banlanced = (self.assets-self.total_debt-self.reserve_profit)<5
#更新财务信息,并根据财务信息判断是否破产
def update_finiance(self):
self.doa = self.total_debt/self.assets #资产负债率
self.ebit = (self.profit+self.interst_cost)/self.interst_cost #利息保障倍数
self.roi = self.profit/self.company_income #净利率
self.inv_tur_rate = self.company_income/self.store_value #存货周转率,收入/存货
#如果资产负债率大于0.8,且利息保障倍数小于1,则破产
if self.doa>=0.8 and self.ebit<=1:
self.endure = False
def make_com_table(self):
##测试用函数
se = pd.Series({
'is_balanced':self.is_banlanced,
'land_reserve_num':self.land_reserve_num,
'land_reserve_price':self.land_reserve_price,
'land_reserve_value':self.land_reserve_value,
'currency_asset':self.currency_asset,
'store_num':self.store_num,
'store_price':self.store_price,
'store_value':self.store_value,
'bank_debt':self.bank_debt,
'other_debt':self.other_debt,
'reserve_profit':self.reserve_profit,
'company_income':self.company_income,
'construction_cost':self.construction_cost,
'other_cost':self.other_cost,
'store_cost':self.store_cost,
'interst_cost':self.interst_cost,
'profit':self.profit,
'avg_debt_rate':self.avg_debt_rate,
'assets':self.currency_asset+self.store_value+self.land_reserve_value,
'debt':self.bank_debt+self.other_debt,
'doa':self.doa
})
self.history = self.history.append(se,ignore_index=True)
四、效果检测
公司初始化和运行所需要的一些参数如下:
parameter_pool={
'loan_year':3,#有息贷款年限
'init_construction_rate':0.8,#初始的建设速率,是土地储备转库存商品的比例
'construction_cost_ration':0.7,#相比于土地购置成本的建设成本支出
'other_cost_ratio':0.2,#相比于营业收入的其他成本支出
'plot_ratio':3,#容积率,按照这个比率将土地储备数量转为库存商品数量
'init_currency_asset_ratio':0.2,#初始化现金比率
'init_land_reserve_value_ratio':0.2, #初始化土地储备价值比例
'init_land_reserve_price':5000,#初始化的土地储备价格
'init_store_value_ratio':0.6,#初始化库存价值比例
'init_store_price':8000,#初始化库存价格
'init_bank_debt_ratio':0.3, #初始化银行负债比例
'init_other_debt_ratio':0.5, #其他负债比例
'init_reserve_profit_ratio':0.2,#初始化留存收益比例
'init_company_loan_rate':0.1,#初始化公司贷款利率
'init_company_income_ratio':0.1, #相对于资产的初始化收入比例
'init_store_cost_ratio':0.06, #初始化存货成本比例
'init_other_cost_ratio':0.02, #初始化其他成本比例
'init_interst_cost_ratio':0.01, #初始化利息成本比例
'deviation_rate':0.1, #由风险偏好程度决定的偏离率
'expected_income_growth_rate_increse':0.02, #由财务风险水平对预期收入增长率的调整力度
'init_bank_vs_other':0.5 #初始化新增债务在银行负债和其他负债之间的比例
}
total_info = {
'company_bank_debt_rate':0.08,#企业贷款利率
'rs_price':10000,#本年房地产成交价,
'last_rs_price':9000,#上年房地产成交价
'rs_roa':0.01,
'land_price':30000, #本年土地市场成交均价
'last_land_price':5000,#上年土地成交均价
#-----------------景气指数-------------------
'prosperity_index_price':0.05,
#----------------运行中的其他数据--------------(可以不放在这里面的,但是写出来吧)
'reserved_company_income_ls':ccc,#一个地产公司收入的集合,需要倒序
}
我们让公司持续运行10年 ,并且假设每年收入都不变(这样的话self.rank就可以根据同一个收入列表来计算了),看看最后的self.history的信息,它记录了财务信息的历史变动,重点关注账目是否平衡,这个history属性在最后的总体研究中是不需要保留的。
#初始化一个公司
com = Company({'name':1,'assets':139733628.6,'risk_preference':'HIGH'})
#让它运行十年
for i in range(10):
com.update_sheet_by_land([{'land_num':20000,'land_price':1000}])
com.update_sheet_by_produce()
com.update_sheet_by_sale([{'sell_price':1397336.286,'sell_num':10}])
com.update_sheet_by_debt()
com.increase_debt()
com.profit_carried_forward()
com.update_finiance()
com.make_com_table()
最后的history信息:
可见的确实现了账目平衡。
如果多设计一些会计科目的话,我们也可以用这个类实现会计做账的功能哦。