Repository: tradingAI/tenvs (language: python)

Issue:
Task
fix np.float 


Discussion / hints:

Fix patch:
diff --git a/Makefile b/Makefile
index 334bdc5..843fed4 100644
--- a/Makefile
+++ b/Makefile
@@ -6,6 +6,7 @@ install:
 test:
 	python3 -m pytest --cov-report=xml:docs/cov/report.xml --cov=tenvs
 	coverage report -m
+	flake8 .
 
 update:
 	git pull origin main
diff --git a/README.md b/README.md
index 278a170..93dc7dd 100644
--- a/README.md
+++ b/README.md
@@ -1,8 +1,5 @@
-![Test](https://github.com/tradingAI/tenvs/workflows/Test/badge.svg?branch=master)
-![Docker](https://github.com/tradingAI/tenvs/workflows/Docker/badge.svg?branch=master)
-[![PyPI version](https://badge.fury.io/py/tenvs.svg)](https://badge.fury.io/py/tenvs)
-[![codecov](https://codecov.io/gh/iminders/tenvs/branch/main/graph/badge.svg?token=3OPPSXMOW1)](https://codecov.io/gh/iminders/tenvs)
-# tenvs
+# tenvs![Test](https://github.com/tradingAI/tenvs/workflows/Test/badge.svg?branch=master)![Docker](https://github.com/tradingAI/tenvs/workflows/Docker/badge.svg?branch=master)[![PyPI version](https://badge.fury.io/py/tenvs.svg)](https://badge.fury.io/py/tenvs)[![codecov](https://codecov.io/gh/iminders/tenvs/branch/main/graph/badge.svg?token=3OPPSXMOW1)](https://codecov.io/gh/iminders/tenvs)
+
 
 
 基于[OpenAI Gym](https://gym.openai.com/)的程序化交易环境模拟器, 旨在为沪深A股基于增强学习的交易算法提供方便使用, 接近真实市场的交易环境
diff --git a/tenvs/accounts/README.md b/tenvs/accounts/README.md
new file mode 100644
index 0000000..cbdc383
--- /dev/null
+++ b/tenvs/accounts/README.md
@@ -0,0 +1,46 @@
+### 股票账户(StockAccount) 设计
+
+每个bar只能提交一个订单:
+- len(volumes) = len(codes) + 1
+- volumes[i] > 0: 表示买入volumes[i]
+- volumes[i] < 0: 表示卖出volumes[i]
+- 需要调用方约束: 可用余额，可卖股票量限制, 以降低实际交易时的撤单率
+
+
+向量化表示成份股的各种属性: 
+- 优点: 使用numpy计算, 向量化操作, 代码少逻辑更清晰, 速度快, 适合回策阶段
+- 缺点: 股票数据必需按照约定顺序组织， 交易量为0的股票即便不必更新计算，也需要进行按0值传入
+
+股票账户的属性繁多，为了清晰表示实现的大致逻辑，将各属性需要更新的阶段用下表表示
+
+|                      | day_init | order_execute | bar_settlement |
+| -------------------- | -------- | ------------- | -------------- |
+| pre_day_total_assets | 1        |               |                |
+| prices               |          |               | 1              |
+| caps                 |          |               | 2              |
+| total_assets         |          |               | 3              |
+| bar_pnl              |          |               | 4              |
+| bar_pnls             |          |               | 5              |
+| day_pnl              | 3        |               | 6              |
+| day_pnls             | 4        |               | 7              |
+| pnl                  |          |               | 8              |
+| pnls                 |          |               | 9              |
+| day_return           | 5        |               | 10             |
+| day_returns          | 6        |               | 11             |
+| value                |          |               | 12             |
+| contributions        |          |               | 13             |
+| weights              |          |               | 14             |
+| day_fees             | 11       | 1             |                |
+| day_fee              | 12       | 2             |                |
+| fee                  |          | 3             |                |
+| bar_cash_changes     |          | 4             |                |
+| day_cash_changes     | 10       | 5             |                |
+| balance              |          | 6             |                |
+| available            | 7        | 7             |                |
+| sellable_volumes     | 8        | 8             |                |
+| frozen_volumes       | 9        | 9             |                |
+| volumes              | 2        | 10            |                |
+|                      |          |               |                |
+|                      |          |               |                |
+|                      |          |               |                |
+|                      |          |               |                |
\ No newline at end of file
diff --git a/tenvs/accounts/const.py b/tenvs/accounts/const.py
index d5d8d8b..d03ac31 100644
--- a/tenvs/accounts/const.py
+++ b/tenvs/accounts/const.py
@@ -24,8 +24,8 @@
 MIN_COMMISSION = 5.0
 
 # 空仓, 股票代号
-NULL_CODE = 'NULL'
-NULL_PRICE = 1.0
+MONEY = 'RMB'
+MONEY_PRICE = 1.0
 # 开市
 TRADE = 1
 # 休市
diff --git a/tenvs/accounts/stk_account.py b/tenvs/accounts/stk_account.py
deleted file mode 100644
index 0946a62..0000000
--- a/tenvs/accounts/stk_account.py
+++ /dev/null
@@ -1,153 +0,0 @@
-import copy
-
-import numpy as np
-from tenvs.accounts.const import (MIN_COMMISSION, NULL_CODE, NULL_PRICE,
-                                  STK_BUY_COMMISSION_RATE,
-                                  STK_SELL_COMMISSION_RATE)
-
-
-class STKAccount:
-    '''
-    (默认)next_bar_open_price: 下一个Bar开盘价撮合
-    current_bar_close_price: 当前Bar收盘价撮合
-    high_low_price: 当前Bar最高最低价撮合
-    vwap: 平均交易价格撮合
-    '''
-
-    def __init__(self, investment=1e5, codes=['000001.SZ']):
-        # 当前持仓股票
-        self.codes = codes
-        # 空仓, 也认为是一只股票
-        self.codes.append(NULL_CODE)
-        # 跟踪股票数
-        self.n = len(codes) + 1
-        self.investment = investment
-        # 当前总资产
-        self.total_assets = investment
-        # 前一bar的总资产
-        self.pre_total_assets = investment
-        # 资金余额
-        self.balance = investment
-        # 可用金额, NOTE(liuwen):可用金额与资金余额并不一定相等(有未成交买进时)
-        self.available = investment
-        # 股票市值(Market Capitalization), 简称Market Cap
-        self.caps = np.zeros((self.n), np.float)
-        self.caps[-1] = investment
-        # 前一市值
-        self.pre_caps = copy.deepcopy(self.caps)
-        # 持仓量
-        self.volumes = np.zeros((self.n), np.float)
-        self.volumes[-1] = investment
-        # 前一bar持仓量
-        self.pre_volumes = np.zeros((self.n), np.float)
-        self.pre_volumes[-1] = investment
-        # 冻结量
-        self.frozen_volumes = np.zeros((self.n), np.float)
-        # 该仓位可卖出股数。T＋1 的市场中sellable = 所有持仓 - 已冻结
-        self.sellable_volumes = np.zeros((self.n), np.float)
-        # 当前价格
-        self.prices = np.zeros((self.n), np.float)
-        self.prices[-1] = NULL_PRICE
-        # 前一价格
-        self.pre_closes = np.zeros((self.n), np.float)
-        self.pre_closes[-1] = NULL_PRICE
-        # 获得该持仓的市场价值在股票账户中所占比例，取值范围[0, 1], 以总账户为基线
-        self.weights = np.zeros((self.n), np.float)
-        self.weights[-1] = 1.0
-        # 当日盈亏
-        self.daily_pnl = 0.0
-        # 当日盈亏
-        self.daily_pnls = np.zeros((self.n), np.float)
-        # 累计盈亏, Profit and Loss(绝对金额)
-        self.pnl = 0.0
-        # 累计盈亏
-        self.pnls = np.zeros((self.n), np.float)
-        # 当前最新一天的日收益率, 以总账户为基线
-        self.daily_return = 0.0
-        self.daily_returns = np.zeros((self.n), np.float)
-
-        # 累计收益率
-        self.return_rate = 0.0
-        self.return_rates = np.zeros((self.n), np.float)
-        # 当日交易费
-        self.daily_fee = 0.0
-        self.daily_fees = np.zeros((self.n), np.float)
-        # 累计交易费
-        self.total_fee = 0.0
-
-    def update_prices(self, prices):
-        """
-        由于市场价格变化，更新相关值
-        NOTE(liuwen): prices[-1] == NULL_PRICE
-        """
-        assert prices[-1] == NULL_PRICE
-        self.pre_closes = copy.deepcopy(self.prices)
-        self.pre_caps = copy.deepcopy(self.caps)
-        self.prices = prices
-        self.caps = self.volumes * self.prices
-        self.total_assets = np.sum(self.caps)
-
-    def update_fees(self, cash_changes):
-        self.daily_fees = [0] * self.n
-        # NOTE(liuwen): NULL_CODE不需要缴费
-        for i in range(self.n - 1):
-            # 卖出
-            if cash_changes[i] > 0:
-                self.daily_fees[i] = max(
-                    cash_changes[i] * STK_SELL_COMMISSION_RATE, MIN_COMMISSION)
-            # 买进
-            elif cash_changes[i] < 0:
-                self.daily_fees[i] = max(
-                    cash_changes[i] * STK_BUY_COMMISSION_RATE, MIN_COMMISSION)
-        self.daily_fee = np.sum(self.daily_fees)
-        self.total_fee += self.daily_fee
-
-    def before_trade(self, adjusts: np.array, status=np.array, bars=np.array):
-        '''
-        bars.shape = (self.n, 5), 5=4(ohlc) + 1(vwap)
-        '''
-        pass
-
-    def check_buy_orders(self, volumes: np.array, prices: np.array):
-        # is_buys: True->买进, False-> 卖出/不交易
-        is_buys = volumes > 0
-        expected_buy_cash = volumes[is_buys] * prices[is_buys]
-        assert self.available > np.sum(expected_buy_cash) * \
-            (STK_BUY_COMMISSION_RATE + 1)
-
-    def check_sell_orders(self, volumes: np.array, prices: np.array):
-        # is_sells: True-> 卖出, False-> 买进/不交易
-        is_sells = volumes < 0
-        check_list = volumes[is_sells] <= self.sellable_volumes
-        assert np.all(check_list)
-
-    def check_null_code_volume(self, volumes: np.array):
-        assert volumes[-1] + self.available > 0
-
-    def trade(self, volumes: np.array, prices: np.array) -> np.array:
-        '''
-        volumes.shape: (self.n,), 包含了NULL_CODE
-        prices.shape: (self.n,), 包含了NULL_CODE
-        (默认)保守交易:
-            1. 先根据self.available 决定买入量
-            2. 再根据self.sellable_volumes 决定卖出量
-        乐观交易:
-            1. 先根据self.sellable_volumes 进行卖出，以获得更多的cash
-            2. 再根据self.available 决定买入量
-        '''
-        self.check_buy_orders(volumes[:-1], prices[:-1])
-        self.check_sell_orders(volumes[:-1], prices[:-1])
-        self.check_null_code_volume(volumes)
-        # volume > 0: 买进, cash_changes < 0
-        # volume < 0: 卖出, cash_changes > 0
-        cash_changes = -volumes * prices
-        self.update_fees(cash_changes)
-        cash_changes = cash_changes - self.daily_fees
-        # 金额变化量
-        self.available += np.sum(cash_changes[:-1])
-        self.volumes += volumes
-        self.volumes[-1] = self.available
-        self.update_prices(prices)
-
-    def after_trade(self, bars: np.array):
-        pass
diff --git a/tenvs/accounts/stock_account.py b/tenvs/accounts/stock_account.py
new file mode 100644
index 0000000..2423f6a
--- /dev/null
+++ b/tenvs/accounts/stock_account.py
@@ -0,0 +1,245 @@
+import copy
+
+import numpy as np
+from tenvs.accounts.const import (MIN_COMMISSION, MONEY, MONEY_PRICE,
+                                  STK_BUY_COMMISSION_RATE,
+                                  STK_SELL_COMMISSION_RATE)
+from tenvs.common.logger import logger
+
+
+class StockAccount:
+    '''
+    NOTE(liuwen):
+        每个bar只能提交一个订单:
+            len(volumes) = len(codes) + 1
+            volumes[i] > 0: 表示买入volumes[i]
+            volumes[i] < 0: 表示卖出volumes[i]
+            需要调用方约束: 可用余额，可卖股票量限制, 以降低实际交易时的撤单率
+    (默认)bar_open_price: Bar开盘价撮合
+    TODO(liuwen):
+        bar_high_low_price: 当前Bar最高最低价撮合(Limit Price order)
+        bar_vwap: 平均交易价格撮合(适合有算法交易执行情况下)
+        bar_twap: 时间平均交易价格撮合(适合有算法交易执行情况下)
+        bar_close_price: 当前Bar收盘价撮合
+    TODO(liuwen): 验证逻辑正确性之后去除 assert
+    '''
+
+    def __init__(self, investment=1e5, codes=['000001.SZ']):
+        # 当前持仓股票
+        self.codes = codes
+        # 空仓, 也认为是一只股票
+        self.codes.append(MONEY)
+        # 跟踪股票数
+        self.n = len(codes)
+        logger.info(f'codes: {codes}, {self.n}')
+        self.investment = investment
+        # 当前总资产
+        self.total_assets = investment
+        # 前一天总资产
+        self.pre_day_total_assets = investment
+        # 资金余额
+        self.balance = investment
+        # 可用金额, 有未成交买进时与资金余额不相等
+        self.available = investment
+        # 股票市值(Market Capitalization), 简称Market Cap
+        self.caps = np.zeros((self.n), float)
+        self.caps[-1] = investment
+        # 资金变化
+        self.bar_cash_changes = np.zeros((self.n), float)
+        # 持仓量
+        self.volumes = np.zeros((self.n), float)
+        self.volumes[-1] = investment
+        # 冻结量
+        self.frozen_volumes = np.zeros((self.n), float)
+        # 该仓位可卖出股数。T＋1 的市场中sellable = 所有持仓 - 已冻结
+        self.sellable_volumes = np.zeros((self.n), float)
+        # 当前价格
+        self.prices = np.zeros((self.n), float)
+        self.prices[-1] = MONEY_PRICE
+        # 获得该持仓的市场价值在股票账户中所占比例，取值范围[0, 1], 以总账户为基线
+        self.weights = np.zeros((self.n), float)
+        self.weights[-1] = 1.0
+        self.bar_pnl = 0.0
+        self.bar_pnls = np.zeros((self.n), float)
+        # 当日盈亏
+        self.day_pnl = 0.0
+        # 当日盈亏
+        self.day_pnls = np.zeros((self.n), float)
+        # 累计盈亏, Profit and Loss(绝对金额)
+        self.pnl = 0.0
+        # 累计盈亏
+        self.pnls = np.zeros((self.n), float)
+        # 当前最新一天的日收益率, 以总账户为基线
+        self.day_return = 0.0
+        self.day_returns = np.zeros((self.n), float)
+        # 净值
+        self.value = 1.0
+        self.contributions = np.zeros((self.n), float)
+        self.weights = np.zeros((self.n), float)
+        self.weights[-1] = 1.0
+        # 当日交易费
+        self.day_fee = 0.0
+        self.day_fees = np.zeros((self.n), float)
+        self.day_cash_changes = np.zeros((self.n), float)
+        # 累计交易费
+        self.fee = 0.0
+
+    def day_init(self, pre_day_closes: np.array):
+        '''
+        每天交易前需要调用
+        pre_closes.shape = (self.n,)
+        '''
+        assert pre_day_closes[-1] == MONEY_PRICE
+        self.pre_day_total_assets = self.total_assets
+        # 如果有拆分股票，则按市值不变进行调整
+        self.volumes = self.caps / pre_day_closes
+        # 当日盈亏
+        self.day_pnl = 0.0
+        self.day_pnls = np.zeros((self.n), float)
+        # 当前最新一天的日收益率, 以总账户为基线
+        self.day_return = 0.0
+        self.day_returns = np.zeros((self.n), float)
+        # 可用资金
+        self.available = self.balance
+        # 可卖
+        self.sellable_volumes = copy.deepcopy(self.volumes)
+        # 冻结量
+        self.frozen_volumes = np.zeros((self.n), float)
+        # 当天交易变化金额
+        self.day_cash_changes = np.zeros((self.n), float)
+        # 当日交易费
+        self.day_fees = np.zeros((self.n), float)
+        self.day_fee = 0.0
+        self.bar_cash_changes = 0.0
+
+    def update_fees(self, cash_changes):
+        # TODO(liuwen): 优化计算逻辑(if-else => vector op)
+        fees = [0] * self.n
+        # NOTE(liuwen): MONEY不需要缴费
+        for i in range(self.n - 1):
+            # 卖出
+            if cash_changes[i] > 0:
+                fees[i] = max(
+                    cash_changes[i] * STK_SELL_COMMISSION_RATE, MIN_COMMISSION)
+            # 买进
+            elif cash_changes[i] < 0:
+                fees[i] = max(
+                    cash_changes[i] * STK_BUY_COMMISSION_RATE, MIN_COMMISSION)
+        fees = np.around(fees, 6)
+        self.day_fees += fees
+        self.day_fee = np.sum(self.day_fees)
+        self.fee += self.day_fee
+
+    def check_buy_orders(self, volumes: np.array, prices: np.array):
+        # fill sell orders by 0
+        filled_volumes = np.maximum(volumes, np.zeros((self.n-1), float))
+        # 不包含最后一项(MONEY项)
+        expected_buy_cash = np.sum((filled_volumes * prices))
+        assert self.available > np.sum(expected_buy_cash) * \
+            (STK_BUY_COMMISSION_RATE + 1)
+
+    def check_sell_orders(self, volumes: np.array):
+        assert np.all((volumes[:-1] + self.sellable_volumes[:-1]) >= 0)
+
+    def check_money_volume(self, volumes: np.array):
+        assert volumes[-1] + self.available > 0
+
+    def bar_settlement(self, closes):
+        """
+        bar结束时，根据bar.closes进行结算, 计算pnls
+        NOTE(liuwen): prices[-1] == MONEY_PRICE
+        """
+        assert closes[-1] == MONEY_PRICE
+        self.prices = closes
+        pre_bar_caps = copy.deepcopy(self.caps)
+        self.caps = np.around(self.volumes * self.prices, 6)
+        pre_bar_total_assets = self.total_assets
+        assert pre_bar_total_assets > 0
+        self.total_assets = np.sum(self.caps)
+        # pnl
+        self.bar_pnl = self.total_assets - pre_bar_total_assets
+        self.bar_pnl = np.around(self.bar_pnl, 6)
+        self.bar_pnls = self.caps + self.bar_cash_changes - pre_bar_caps
+        self.bar_pnls = np.around(self.bar_pnls, 6)
+        # MONEY pnl为0
+        self.bar_pnls[-1] = 0
+        assert np.around(self.bar_pnl, 6) == np.around(
+            np.sum(self.bar_pnls), 6)
+        self.day_pnl += self.bar_pnl
+        self.day_pnls += self.bar_pnls
+        self.pnl += self.bar_pnl
+        self.pnls += self.bar_pnls
+        # return rate
+        self.day_return = self.day_pnl / self.pre_day_total_assets
+        self.day_returns = self.day_pnls / self.pre_day_total_assets
+        self.value = np.around(self.pnl / self.investment + 1.0, 8)
+        self.contributions = np.around(self.pnls / self.investment, 8)
+        # 权重
+        self.weights = self.caps / self.total_assets
+
+    def bar_log(self, day, id):
+        logger.info(
+            f'day={day}, bar={id}, bar_pnl={self.bar_pnl: .2f}')
+
+    def day_log(self, day):
+        logger.info(f'day={day}, day_pnl={self.day_pnl: .2f}')
+        logger.info(f'day={day}, available={self.available: .2f}')
+
+    def bar_execute(self, volumes: np.array, bar: dict,
+                    bar_id=0, day_log=False, day='20210101') -> np.array:
+        '''
+        volumes.shape: (self.n,), 包含了MONEY
+        bar:
+            pre_bar_closes.shape: (self.n,), 包含了MONEY
+            opens.shape: (self.n,)
+            pre_day_close.shape: (self.n)
+            closes.shape: (self.n,)
+            highs.shape: (self.n,) # 暂不需要
+            lows.shape: (self.n,) # 暂不需要
+            vwap.shape: (self.n,) # 暂不需要
+
+        (默认)保守交易:
+            1. 先根据self.available 决定买入量
+            2. 再根据self.sellable_volumes 决定卖出量
+        乐观交易:
+            1. 先根据self.sellable_volumes 进行卖出，以获得更多的cash
+            2. 再根据self.available 决定买入量
+        '''
+        volumes = volumes.astype(float)
+        assert len(volumes) == self.n
+        if bar_id == 0:
+            self.day_init(bar['pre_day_close'])
+        prices = bar['opens']
+        self.check_money_volume(volumes)
+        self.check_buy_orders(volumes[:-1], prices[:-1])
+        self.check_sell_orders(volumes)
+
+        # volume > 0: 买进, cash_changes < 0
+        # volume < 0: 卖出, cash_changes > 0
+        self.bar_cash_changes = -volumes * prices
+        self.update_fees(self.bar_cash_changes)
+        self.bar_cash_changes -= self.day_fees
+
+        # 金额变化量
+        total_cash_change = np.around(np.sum(self.bar_cash_changes[:-1]), 6)
+        # MONEY
+        self.bar_cash_changes[-1] = total_cash_change
+        self.day_cash_changes += self.bar_cash_changes
+        self.balance = np.around(self.balance + total_cash_change, 6)
+        self.available = np.around(self.available + total_cash_change, 6)
+        volumes[-1] = total_cash_change
+        self.volumes = np.around(self.volumes + volumes, 6)
+        # 可卖， 减去卖出的部分
+        self.sellable_volumes += np.minimum(volumes,
+                                            np.zeros((self.n), float))
+        self.sellable_volumes = np.around(self.sellable_volumes, 6)
+        # 冻结, 加上买进的部分
+        self.frozen_volumes += np.maximum(volumes,
+                                          np.zeros((self.n), float))
+        logger.info(self.volumes[-1])
+        logger.info(self.available)
+        assert self.volumes[-1] == self.available
+        self.bar_settlement(bar['closes'])
+        self.bar_log(day, bar_id)
+        if day_log:
+            self.day_log(day)


Test patch:
diff --git a/tenvs/accounts/stock_account_test.py b/tenvs/accounts/stock_account_test.py
new file mode 100644
index 0000000..9297db4
--- /dev/null
+++ b/tenvs/accounts/stock_account_test.py
@@ -0,0 +1,164 @@
+import sys
+
+import numpy as np
+import pytest
+from tenvs.accounts.stock_account import StockAccount
+
+
+def mock_bars(day_num=10):
+    closes1 = np.reshape(np.linspace(10.0, 11, day_num + 1), (day_num + 1, 1))
+    closes2 = np.reshape(np.linspace(10.0, 11, day_num + 1), (day_num + 1, 1))
+    closes3 = np.ones((day_num + 1, 1))
+    closes = np.concatenate([closes1, closes2, closes3], axis=1)
+    bars = {}
+    for day in range(day_num):
+        bars[str(day)] = {
+            'pre_day_close': closes[day, :],
+            'pre_bar_closes': closes[day, :],
+            'closes': closes[day + 1, :],
+            'opens': closes[day, :]}
+    return bars
+
+
+class TestStockAccount:
+
+    def test_basic(self):
+        np.random.seed(0)
+        investment = 1e5
+        codes = ['000001.SZ', '000001.SZ']
+        account = StockAccount(investment, codes)
+        assert account.investment == investment
+        assert account.pre_day_total_assets == investment
+        assert account.caps.tolist() == [0, 0, investment]
+        assert account.total_assets == investment
+        assert account.bar_pnl == 0.0
+        assert account.bar_pnls.tolist() == [0.0, 0.0, 0.0]
+        assert account.day_pnl == 0.0
+        assert account.day_pnls.tolist() == [0.0, 0.0, 0.0]
+        assert account.pnl == 0.0
+        assert account.pnls.tolist() == [0.0, 0.0, 0.0]
+        assert account.day_return == 0
+        assert account.day_returns.tolist() == [0.0, 0.0, 0.0]
+        assert account.value == 1.0
+        assert account.contributions.tolist() == [0.0, 0.0, 0.0]
+        assert account.weights.tolist() == [0.0, 0.0, 1.0]
+        assert account.day_fee == 0
+        assert account.day_fees.tolist() == [0.0, 0.0, 0.0]
+        assert account.fee == 0
+        assert account.bar_cash_changes.tolist() == [0.0, 0.0, 0.0]
+        assert account.day_cash_changes.tolist() == [0.0, 0.0, 0.0]
+        assert account.balance == investment
+        bars = mock_bars(10)
+        # 各买入500股
+        day = '0'
+        volumes = np.array([500, 500, 0])
+        account.bar_execute(
+            volumes=volumes, bar=bars[day], bar_id=0, day_log=True, day=day)
+        assert account.available == 89990.0
+        assert account.pre_day_total_assets == investment
+        assert account.caps.tolist() == [5050.0, 5050.0, 89990.0]
+        assert account.total_assets == 100090.0
+        assert account.bar_pnl == 90.0
+        assert account.bar_pnls.tolist() == [45.0, 45.0, 0.0]
+        assert account.day_pnl == 90.0
+        assert account.day_pnls.tolist() == [45.0, 45.0, 0.0]
+        assert account.day_pnl == 90.0
+        assert account.day_pnls.tolist() == [45.0, 45.0, 0.0]
+        assert account.day_return == 0.0009
+        assert account.day_returns.tolist() == [0.00045, 0.00045, 0.0]
+        assert account.value == 1.0009
+        assert account.contributions.tolist() == [0.00045, 0.00045, 0.0]
+        assert account.weights.tolist() == [
+            0.0504545908682186, 0.0504545908682186, 0.8990908182635627]
+        assert account.day_fee == 10.0
+        assert account.day_fees.tolist() == [5.0, 5.0, 0.0]
+        assert account.fee == 10.0
+        assert account.bar_cash_changes.tolist(
+        ) == [-5005.0, -5005.0, -10010.0]
+        assert account.day_cash_changes.tolist(
+        ) == [-5005.0, -5005.0, -10010.0]
+        assert account.balance == 89990.00
+        assert account.available == 89990.00
+        assert account.sellable_volumes.tolist() == [0., 0., 89990.0]
+        assert account.frozen_volumes.tolist() == [500., 500., 0.]
+        assert account.volumes.tolist() == [500., 500., 89990.0]
+        # 再次买入
+        volumes = np.array([6000, 500, 0])
+        day = '1'
+        account.bar_execute(
+            volumes=volumes, bar=bars[day], bar_id=0, day_log=True, day=day)
+        assert account.available == 24330.0
+        assert account.pre_day_total_assets == 100090.0
+        assert account.caps.tolist() == [66300.0, 10200.0, 24330.0]
+        assert account.total_assets == 100830.0
+        assert account.bar_pnl == 740.0
+        assert account.bar_pnls.tolist() == [645.0, 95.0, 0.0]
+        assert account.day_pnl == 740.0
+        assert account.day_pnls.tolist() == [645.0, 95.0, 0.0]
+        assert account.day_pnl == 740.0
+        assert account.day_pnls.tolist() == [645.0, 95.0, 0.0]
+        assert account.day_return == 0.007393345988610251
+        assert account.day_returns.tolist(
+        ) == [0.006444200219802178, 0.0009491457688080727, 0.0]
+        assert account.value == 1.0083
+        assert account.contributions.tolist() == [0.0069, 0.0014, 0.0]
+        assert account.weights.tolist() == [
+            0.6575423980958048, 0.10116036893781613, 0.24129723296637906]
+        assert account.day_fee == 10.0
+        assert account.day_fees.tolist() == [5.0, 5.0, 0.0]
+        assert account.fee == 20.0
+        assert account.bar_cash_changes.tolist(
+        ) == [-60605.0, -5055.0, -65660.0]
+        assert account.day_cash_changes.tolist(
+        ) == [-60605.0, -5055.0, -65660.0]
+        assert account.balance == 24330.0
+        assert account.available == 24330.0
+        assert account.sellable_volumes.tolist() == [500.0, 500.0, 24330.0]
+        assert account.frozen_volumes.tolist() == [6000.0, 500.0, 0.0]
+        assert account.volumes.tolist() == [6500.0, 1000.0, 24330.0]
+        # 卖出
+        volumes = np.array([-5000, 500, 0])
+        day = '2'
+        account.bar_execute(
+            volumes=volumes, bar=bars[day], bar_id=0, day_log=True, day=day)
+        assert account.available == 70129.222
+        assert account.pre_day_total_assets == 100830.0
+        assert account.caps.tolist() == [15450.0, 15450.0, 70129.222]
+        assert account.total_assets == 101029.222
+        assert account.bar_pnl == 199.222
+        assert account.bar_pnls.tolist() == [
+            54.222, 145.0, 0.0]
+        assert account.day_pnl == 199.222
+        assert account.day_pnls.tolist() == [
+            54.222, 145.0, 0.0]
+        assert account.day_pnl == 199.222
+        assert account.day_pnls.tolist() == [
+            54.222, 145.0, 0.0]
+        assert account.day_return == 0.0019758206882872164
+
+        assert account.day_returns.tolist(
+        ) == [0.0005377566200535555, 0.0014380640682336607, 0.0]
+        assert account.value == 1.01029222
+        assert account.contributions.tolist() == [0.00744222, 0.00285, 0.0]
+        assert account.weights.tolist() == [
+            0.1529260514349007, 0.1529260514349007, 0.6941478971301986]
+
+        assert account.day_fee == 100.778
+        assert account.day_fees.tolist() == [95.778, 5.0, 0.0]
+        assert account.fee == 120.778
+
+        assert account.bar_cash_changes.tolist(
+        ) == [50904.222, -5105.0, 45799.222]
+        assert account.day_cash_changes.tolist(
+        ) == [50904.222, -5105.0, 45799.222]
+        assert account.balance == 70129.222
+        assert account.available == 70129.222
+
+        assert account.sellable_volumes.tolist(
+        ) == [1500.0, 1000.0, 24330.0]
+        assert account.frozen_volumes.tolist() == [0.0, 500.0, 45799.222]
+        assert account.volumes.tolist() == [1500.0, 1500.0, 70129.222]
+
+
+if __name__ == "__main__":
+    sys.exit(pytest.main([__file__]))