相場商売

トレーディングビジネスあれこれ

19章

システムの成績を出す部分。
ensure_closeのメソッドを少し変更した。

libフォルダのstats.py

#!/usr/bin/env python
# -*- coding: utf-8 -*-

from trade import Trade
from myarray import Array

# Statsクラス
# 取引結果から各種統計を計算
class Stats:
    
    def __init__(self, trade):
        self.trades = trade
        
    def sum_profit(self):
        return sum(self._profits())
    
    def average_profit(self):
        return (self._profits().average())
    
    def wins(self):
        return len([profit for profit in self._profits() if profit > 0])
    
    def losses(self):
        return len([profit for profit in self._profits() if profit > 0])       

    def draws(self):
        return len([profit for profit in self._profits() if profit == 0])
    
    def winning_percentage(self):
        return self.wins() / len(self._profits())
        
    def profit_factor(self):
        if self.losses() == 0:
            return None
        else:
            total_profit = sum([profit for profit in self._profits() if profit > 0])
            total_loss   = sum([profit for profit in self._profits() if profit < 0])
            return total_profit / abs(total_loss)
    
    def sum_r(self):
        if any(self._r_multiples()):
            return sum(self._r_multiples())
        
    def average_r(self):
        if any(self._r_multiples()):
            return self._r_multiples().average()
        
    def sum_percentage(self):
        return sum(self._percentages())
    
    def average_percentage(self):
        return self._percentages().average()
    
    def average_length(self):
        return Array([trade.length for trade in self.trades]).average()
    
    def _profits(self):
        if hasattr(self, 'profits') == False:
            self.profits = Array([trade.profit() for trade in self.trades])
        return self.profits
        
    def _r_multiples(self):
        if hasattr(self, 'r_multiples') == False:
            self.r_multiples = Array([trade.r_multiple() for trade in self.trades])            
        return self.r_multiples
        
    def _percentages(self):
        if hasattr(self, 'percentages') == False:
            self.percentages = Array([trade.percentage_result() for trade in self.trades])
        return self.percentages

libフォルダのrecorder.py

#!/usr/bin/env python
# -*- coding: utf-8 -*-

from trade import Trade
from myarray import Array
from stats import Stats
import re
import sys
import csv
import shutil
import os
import errno
from collections import OrderedDict

# 取引の記録を行うクラス
class Recorder:
    
    def __init__(self, record_dir = None):
        self.record_dir = record_dir
        
    # 1銘柄の取引を記録する
    def record_a_stock(self, trades):
        code = trades[0].stock_code
        file_name = '{0}/{1}.csv'.format(self.record_dir, code)
        def func():
            with open(file_name, 'w') as f:
                writer = csv.writer(f, lineterminator='\n')
                writer.writerow([value for value in self._items_for_a_stock().values()])
                for trade in trades:
                    one_trade = []
                    for attr in self._items_for_a_stock().keys():
                        if callable(getattr(trade, attr)):
                            one_trade.append(getattr(trade, attr)())
                        else:
                            one_trade.append(getattr(trade, attr))
                    writer.writerow(one_trade)                 
        self._ensure_close(file_name, func)  
        
    # 銘柄ごとの統計の一覧表の作成
    def record_stats_for_each_stock(self, results):
        file_name = '{0}/_stats_for_each_stock.csv'.format(self.record_dir)        
        def func():
            with open(file_name, 'w') as f:
                writer = csv.writer(f, lineterminator='\n')
                line = [value for value in self._stats_items().values()]
                line.insert(0, 'コード')
                writer.writerow(line)
                for trades in results:
                    line = self._stats_array(trades)
                    line.insert(0, trades[0].stock_code)
                    writer.writerow(line)
        self._ensure_close(file_name, func)
                    
    # すべてのトレードの統計
    def record_stats(self, results):
        file_name = '{0}/_stats.csv'.format(self.record_dir)        
        def func():
            with open(file_name, 'w') as f:
                writer = csv.writer(f, lineterminator='\n')
                writer.writerow([value for value in self._stats_items().values()])
                writer.writerow(self._stats_array(list(self._flatten(results))))                      
        self._ensure_close(file_name, func)       
    
    # 設定ファイルをコピーする
    def record_setting(self, file_name):
        shutil.copyfile(file_name, '{0}/_setting.py'.format(self.record_dir))
    
    # 結果保存用のフォルダを作る
    def create_record_folder(self):
        if os.path.exists(self.record_dir):
            print('記録フォルダ {0} はすでに存在します。上書きしますか? y/n'.format(self.record_dir))
            def func():
                print('上書きします')
            self._yes_check(func)        
        else:
            print('記録フォルダ {0} は存在しません。新しく作りますか? y/n'.format(self.record_dir))
            def func():
                os.mkdir(self.record_dir)
            self._yes_check(func)
                               
    def _items_for_a_stock(self):
        return OrderedDict((('trade_type'       , '取引種別'),
                            ('entry_date'       , '入日付'),
                            ('entry_price'      , '入値'),
                            ('volume'           , '数量'),
                            ('first_stop'       , '初期ストップ'),
                            ('exit_date'        , '出日付'),
                            ('exit_price'       , '出値'),
                            ('profit'           , '損益(円)'),
                            ('r_multiple'       , 'R倍数'),
                            ('percentage_result', '%損益'),
                            ('length'           , '期間')))
        
    def _stats_items(self):
        return OrderedDict((('sum_profit'        , '総損益'),
                            ('wins'              , '勝ち数'),
                            ('losses'            , '負け数'),
                            ('draws'             , '分け数'),
                            ('winning_percentage', '勝率'),
                            ('average_profit'    , '平均損益'),
                            ('profit_factor'     , 'PF'),
                            ('sum_r'             , '総R倍数'),
                            ('average_r'         , '平均R倍数'),
                            ('sum_percentage'    , '総損益率'),
                            ('average_percentage', '平均損益率'),
                            ('average_length'    , '平均期間')))
    
    def _stats_array(self, trades):
        sa = Array([])
        stats = Stats(trades)
        for stats_name in self._stats_items().keys():
            sa.append(getattr(stats, stats_name)() or '-')
        return sa
        
    def _ensure_close(self, file_name, func):
        try:
            func()
        except PermissionError as e:
            if e.errno == errno.EACCES:
                while True:
                    i = input('{0}が他のプログラムで書き込み禁止で開かれている可能性があります。\nファイルを閉じてからエンターキーを押してください。'.format(file_name))
                    if not i:
                        func()                       
                        break                

    def _yes_check(self, func):
        while True:
            i = input('')
            if re.search('^[yY]', i) is not None:
                func()
                break
            elif re.search('^[nN]', i) is not None:
                print('終了します')
                sys.exit()
            else:
                print('y(はい) か  n(いいえ)でお答えください')
            
    def _flatten(self, i):
        for a in i:
            if hasattr(a, '__iter__'):
                for b in self._flatten(a):
                    yield b
            else:
                yield a
            

checkフォルダのrecorder_check.py

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import sys, os
sys.path.append(os.pardir + "\lib")

from trading_system import TradingSystem
from text_to_stock import TextToStock
from rule.entry.estrangement_entry import EstrangementEntry
from rule.exit.stop_out_exit import StopOutExit
from rule.exit.estrangement_exit import EstrangementExit
from rule.stop.average_true_range_stop import AverageTrueRangeStop
from rule.filter.moving_average_direction_filter import MovingAverageDirectionFilter
from recorder import Recorder

data = TextToStock({'data_dir': None, 'stock_list': 'tosho_list.txt', 'market_section': None})
trading_system = TradingSystem({'entries': [EstrangementEntry({'span': 20, 'rate': 5})],
                                'exits'   : [StopOutExit(),
                                            EstrangementExit({'span': 20, 'rate': 3})],
                                'stops'  : [AverageTrueRangeStop({'span': 20, 'ratio': None})],
                                'filters': [MovingAverageDirectionFilter({'span': 30})]})

def simulate(code):
    stock = data.generate_stock(code)
    trading_system.set_stock(stock)
    trading_system.calculate_indicators()
    trade = None
    trades = []
    for i in range(0,len(stock.prices)):
        if trade is not None:
            trading_system.set_stop(trade, i)
            trade.length += 1
        if trade is None:
            trade = trading_system.check_entry(i)
            if trade is not None:
                trade.volume = stock.unit
        if trade is not None:
            trading_system.check_exit(trade, i)
            if trade.closed_check() == True:
                trades.append(trade)
                trade = None
    return trades

recorder = Recorder()
recorder.record_dir = os.pardir + r'\result\test'
recorder.create_record_folder()
recorder.record_setting(__file__)

results = [simulate(code) for code in [4063, 7203, 8604]]
results = [trades for trades in results if len(trades) != 0]

for trades in results:
    recorder.record_a_stock(trades)

recorder.record_stats_for_each_stock(results)
recorder.record_stats(results)