フヨダ

自分の勉強と記憶力改善のために、興味あることを書いていきます なるべく分かりやすく

松井証券のCSVファイルをまとめてくれるプログラム python

こんにちは。松井証券の取引履歴のcsvをダウンロードしたら、適当にまとめてくれるプログラムを作りました。

csvファイルをこう、売り買いをひとまとめにして欲しかったので。

Pythonの練習にやりました。現物取引しか対応してないです。

出力されたファイル

f:id:gajumaru7:20200421004449p:plain

 

f:id:gajumaru7:20200421003538p:plain

こんなふうにディレクトリつくって、csvのフォルダに取引履歴のデータをダウンロードして入れます。

CSVは勝手に古いcsvとまとめてくれます。

 

それで

matuiAggregator.pyを起動すると、csv_uniter.pyが呼び出されて、

このディレクトリに売り買いを1セットとした、売買データが出力されます

csv_uniter.py のコード

#! Python3
# -*- coding: utf-8 -*-
csv_uniter.py - 松井証券の取引履歴のcsvを統合する

import csv, os, sys, datetime, shutil
import logging
#logging.disable(logging.CRITICAL)
logging.basicConfig(level=logging.DEBUG, format="%(asctime)s - %(levelname)s - %(message)s")

logging.debug("csvファイルを探索中...")
f_list = 

for filename in os.listdir(".\\csv"):
    if filename.lower().endswith(".csv"):
        f_list.append(filename)

def cPath(filename):
    dirname = os.path.join(".\\csv", filename)
    return dirname


# {日付: ファイルネーム}の用にデータをリストに突っ込んでいく
csv_data = 



if len(f_list) <= 1:
    logging.debug("ファイルが1つ以下でした。終了します")
else:
        

    new_f = open(cPath("matsui_united_{}.csv".format(datetime.datetime.now().strftime("%Y%m%d%H%M%S"))), "w"newline="")
    new_f_writer = csv.writer(new_f)

    for f in f_list:
        f = open(cPath(f), "r")
        f_reader = csv.reader(f)
        f_data = list(f_reader)
        for i in range(1len(f_data)):
            if f_data[i] in csv_data:
                continue
            csv_data.append(f_data[i])

    csv_dataをすべて、sort可能なdatetimeオブジェクトにする
    for i in range(len(csv_data)):
        csv_data[i][0] = datetime.datetime.strptime(csv_data[i][0], "%Y/%m/%d")
    # 時間を元にソートする、新しい日付が上になるように、reverseで降順にする
    csv_data.sort(key=lambda x: x[0], reverse=True)
    # 元のフォーマットに戻す
    for i in range(len(csv_data)):
        csv_data[i][0] = csv_data[i][0].strftime("%Y/%m/%d")

    # データを貼り付ける
    new_f_writer.writerow(f_data[0])
    new_f_writer.writerows(csv_data)


    f.close()
    new_f.close()
    # 最後に参照ファイルをpastフォルダに入れる
    for file in f_list:
        shutil.move(cPath(file), ".\\csv\\past")

logging.debug("csv処理完了")

 

matuiAgrregater.pyのコード

#! Python3
# -*- coding: utf-8 -*-
# matuiAggregater.py - 松井証券の取引履歴のcsvを読み込んで集計してくれるプログラム

import csv, os, shutil
from openpyxl.utils import get_column_letter, column_index_from_string

import logging, openpyxl, datetime
#logging.disable(logging.CRITICAL)
logging.basicConfig(level=logging.DEBUG, format="%(asctime)s - %(levelname)s - %(message)s")

import csv_uniter

logging.debug("xlsxファイルを生成します")

# 古いxlsxデータは.\\past_xlsxにいれる
for xlsxfile in os.listdir("."):
    if xlsxfile.lower().endswith(".xlsx"):
        logging.debug("同フォルダ内の古いxlsxファイルをpast_xlsxに移動します")
        shutil.move(xlsxfile, ".\\past_xlsx")


for filename in os.listdir(".\\csv"):
    if filename.lower().endswith(".csv"):
        csvfilename = filename
def cPath(file):
    return os.path.join(".\\csv"file)

savename = "matui_matome_{}.xlsx".format(datetime.datetime.now().strftime("%Y%m%d%H%M%S"))
f = open(cPath(csvfilename))
f_reader = csv.reader(f)
f_data = list(f_reader)

# 2つの視点、セッターとチェッカーとする。セッターは主観。チェッカーはペアを探す視点。チェッカーにマッチしたらchecked_row_listにいれて、その行をセッターはスキップする
checked_row_list = 
only_buy_list = 

# 貼り付け先はxlsxファイルにする
wb = openpyxl.Workbook()
sheet = wb.active
# データの型は{取引区分0, 銘柄1, コード2, 買付日3, 買値4, 売却日5, 売値6, 保有日数7, 損益率8, 損益額9}
data_type_order = ["取引区分""銘柄""コード""買付日""売却日""保有日数","買数量""売数量""買値""売値""損益率","損益額""コメント"]



# エクセルシートを新規作成したので、data_type_orderリストに従って1列目を埋める
for Col in range(1len(data_type_order)+ 1):
    sheet.cell(1, Col).value = data_type_order[Col - 1]
# 一列目はウィンドウ固定する
sheet.freeze_panes = "A2"

# 受け渡し日ごとにまとまった列のブロックとして、それを古い日から順番にサーチしていく。 {受渡日: row}
day_row_dict = {}
for row in range(1len(f_data)):
    day_row_dict.setdefault(datetime.datetime.strptime(f_data[row][0], "%Y/%m/%d"), )
    day_row_dict[datetime.datetime.strptime(f_data[row][0], "%Y/%m/%d")].append(row)
# 念のため日付をソートする. 古い日が先頭に来る reverse=False
day_row_key = sorted(day_row_dict, reverse=False)


def pasteExcel(data):
    """辞書データを受け取って、excelの先頭行に貼り付ける"""
    # エクセルシートに貼り付ける
    sheet.insert_rows(2)
    # エクセルの第一列目を参照して、貼り付ける
    for Col in range(1, sheet.max_column + 1):
        try:
            sheet.cell(2, Col).value = data[sheet.cell(1, Col).value]
        # dataにないキーがでたらスキップする
        except KeyError:
            continue

def pareset(ROW1ROW2):
    """データを並びかえて作る ROW1が買い、ROW2が売却。売りだけの場合も、買いだけの場合もこれを呼び出すこと"""
    data = {}
    data["コメント"] = ""

    if ROW1 ==  and ROW2:
        #売りでだけの時
        data["取引区分"] = f_data[ROW2[0]][9]
        data["コード"] = f_data[ROW2[0]][2]
        data["銘柄"] = f_data[ROW2[0]][3]
        data["売却日"] = f_data[ROW2[-1]][1]
        sell_sum = 0
        sell_num = 0
        for i in range(len(ROW2)):
            sell_sum += int(f_data[ROW2[i]][14])
            sell_num += int(f_data[ROW2[i]][10])
        data["売値"] = sell_sum
        data["売数量"] = sell_num
        data["損益額"] = sell_sum
        data["コメント"] += "買いデータが足りません"
    
    elif ROW1 and ROW2 == :
        #買いだけの時
        data["取引区分"] = f_data[ROW1[0]][9]
        data["コード"] = f_data[ROW1[0]][2]
        data["銘柄"] = f_data[ROW1[0]][3]
        data["買付日"] = f_data[ROW1[0]][1]
        buy_sum = 0
        buy_num = 0
        for i in range(len(ROW1)):
            buy_sum += int(f_data[ROW1[i]][14])
            buy_num += int(f_data[ROW1[i]][10])        
        data["買値"] = buy_sum
        data["買数量"] = buy_num
        data["損益額"] = buy_sum

    # 売り買いペアであるとき
    elif ROW1 and ROW2:

    
        data["取引区分"] = f_data[ROW2[0]][9]
        data["コード"] = f_data[ROW2[0]][2]
        data["銘柄"] = f_data[ROW2[0]][3]
        # 複数の場合は最後に売却した日が売り
        data["売却日"] = f_data[ROW2[-1]][1]
        #売値, 買値、株数はリストの受け渡し金額を合算する
        sell_sum = 0
        sell_num = 0
        buy_sum = 0
        buy_num = 0
        for i in range(len(ROW2)):
            sell_sum += int(f_data[ROW2[i]][14])
            sell_num += int(f_data[ROW2[i]][10])
        data["売値"] = sell_sum
        data["売数量"] = sell_num
        data["買付日"] = f_data[ROW1[0]][1]    
        for i in range(len(ROW1)):
            buy_sum += int(f_data[ROW1[i]][14])
            buy_num += int(f_data[ROW1[i]][10])
        data["買値"] = buy_sum
        data["買数量"] = buy_num
        保有日数はdatetimeを使って計算する.datetimeを引き算するとtimedeltaオブジェクトができる
        date1 = datetime.datetime.strptime(f_data[ROW1[0]][1], "%Y/%m/%d")
        date2 = datetime.datetime.strptime(f_data[ROW2[-1]][1], "%Y/%m/%d")
        data["保有日数"] = int((date2 - date1).days)
        # +-がすでに付加されているので注意
        data["損益額"] = int(data["買値"]) + int(data["売値"])
        # 損益率は 損益額/ 買値(絶対値) * 100 単位は%
        data["損益率"] = round(int(data["損益額"]) / abs(int(data["買値"])) * 1001)
        if len(ROW2) > 1:
            data["コメント"] += "分割売り. "
        if len(ROW1) > 1:
            data["コメント"] += "買い増し. " 
        if sell_num > buy_num:
            data["コメント"] += "買いデータが足りません"
    else:
        raise Exception("pareset()に例外があります")
    pasteExcel(data)

def notPhyTrans(ROW1):
    """現物取引以外の項目をxlデータに貼り付ける"""
    data = {}
    data["取引区分"] = f_data[ROW1][9]
    data["買付日"] = f_data[ROW1][0]
    data["コード"] = f_data[ROW1][2]
    data["銘柄"] = f_data[ROW1][3]
    data["損益額"] = int(f_data[ROW1][14])

    pasteExcel(data)

for date in day_row_key:
    for Row in day_row_dict[date]:


    # メインの視点Row: CSVデータの一番下から上に向かって、スキャンする
        
        #後述のペア探しチェッカーがチェック済みならスキップする
        if Row in checked_row_list:
            continue
        # 取引区分が現物以外の配当金等ならペアを探さない個別で表示する
        elif f_data[Row][9] != "現物":
            #単体で新しいワークブックの列の一番上に挿入
            notPhyTrans(Row)
            continue
        # 現物かつ、売りで始まっていたら、買いが空白のデータとしてエクセルの一番上に挿入。このCSVの期間には、買いのデータが乗っていないのだ。
        elif f_data[Row][7] == "売":
            # 買いが空白のデータを一番上に送る
            pareset(ROW1=ROW2=[Row])
            continue
        elif f_data[Row][9] == "現物" and f_data[Row][7] == "買":
            checked_row_list.append(Row)
            # 現物かつ買いなので、チェッカーが売りのペアを探す。チェッカーの売りの数量を設定しておく
            # 残株数
            num_stocks_remaining = int(f_data[Row][10])

            # チェッカーで探した同一株を探し続ける。買った株数に追いつくまで。また買った株数が0になる前に買えば残株数に加算され、さらに売りを探す。
            # 最後まで売り切っていなければ一番最後にエクセルの上に挿入しないといけない
            multiple_checker_sell_rows = []
            multiple_checker_buy_rows = [Row]

            # チェッカー: checker_row がペアを探す。ペアのRowはチェック済みにする
            for checker_date in day_row_key[day_row_key.index(date):]:
                for checker_row in day_row_dict[checker_date]:
                # ペアもしくはxl反映済みの列はスキップする
                    if checker_row in checked_row_list:
                        continue
                    # 条件に合うペアを探す
                    if (f_data[Row][2] == f_data[checker_row][2]) and f_data[checker_row][7] == "売":            
                        # マッチしたものは、メインのRowやチェッカーのchecker_rowでスキップできるようにする
                        checked_row_list.append(checker_row)
                        multiple_checker_sell_rows.append(checker_row)
                        num_stocks_remaining -= int(f_data[checker_row][10])
                        if num_stocks_remaining == 0:
                            # ペアとみなして、エクセルデータにペアのデータを渡して、チェック済みにする            
                            pareset(ROW1=multiple_checker_buy_rows, ROW2=multiple_checker_sell_rows)
                            break
                        elif num_stocks_remaining > 0:
                            continue
                        else:
                            # なぜか売りの方が多い、CSVデータに買いがない
                            pareset(ROW1=multiple_checker_buy_rows, ROW2=multiple_checker_sell_rows)
                            

                    elif (f_data[Row][2] == f_data[checker_row][2]) and f_data[checker_row][7] == "買":
                        checked_row_list.append(checker_row)
                        multiple_checker_buy_rows.append(checker_row)
                        # 残株数が0になる前にまた買いがあれば、残株数を増やす。
                        num_stocks_remaining += int(f_data[checker_row][10])
                        continue
                    else:
                        # この列はチェッカーに引っ掛からなかった
                        continue
                
                else:
                    #1つのdate内でがブレイクなしに終わった
                    continue
                # チェッカーがブレイクして終わったらチェッカーのループを抜ける
                break
            
            else:
                #チェッカーがすべての列を見終わったのに、残株数がある場合
                if num_stocks_remaining > 0:
                    #TODO: 一番最後に一番上に持っていく, 残株数を表示したい
                    only_buy_list.append*1
        else:
            raise Exception("メイン視点で例外が存在しました")


# まだ売却していないものは上に持っていく
for row1, row2 in only_buy_list:
    pareset(ROW1=row1, ROW2=row2)
              

wb.save(savename)
f.close()


logging.debug("xlsx処理完了")

    
    

 



*1:multiple_checker_buy_rows, multiple_checker_sell_rows