立花証券・ｅ支店・ＡＰＩのラッパー

ｅ支店のAPIはクセが強めで、ぜんぜんリーダブルではないんですが、ユーザー向けツールの拡張的位置づけの某kabuステと違って、自社のサービス向けにしっかりと開発されている印象です。
抽象度を上げるラッパーさえ書いてしまえば、なんてことはない。

しかし、IssueもほったらかしのkabuステのAPIは今後どうなるんでしょうか。
一般信用の貸借銘柄が豊富なのは魅力なので、ぜひ頑張ってほしい所です。

```python ebranchapi/__init__.py
import requests
import datetime as dt
import json
from ebranchapi import get_market_price

class Context(object):
    def __init__(
        self,
        base_url=None,
        user_id=None,
        password=None,
    ):
        self.base_url = base_url
        self.user_id = user_id
        self.password = password
        self.request_no = 1
        self.session_info = None
        self.mapping = {
            'xLISS': '所属',
            'pDPP': '現在値',
            'tDPP:T': '現在値時刻',
            'pDPG': '現値前値比較',
            'pDYWP': '前日比',
            'pDYRP': '騰落率',
            'pDOP': '始値',
            'tDOP:T': '始値時刻',
            'pDHP': '高値',
            'tDHP:T': '高値時刻',
            'pDLP': '安値',
            'tDLP:T': '安値時刻',
            'pDV': '出来高',
            'pQAS': '売気配値種類',
            'pQAP': '売気配値',
            'pAV': '売気配数量',
            'pQBS': '買気配値種類',
            'pQBP': '買気配値',
            'pBV': '買気配数量',
            'xDVES': '配当落銘柄区分',
            'xDCFS': '不連続要因銘柄区分',
            'pDHF': '日通し高値フラグ',
            'pDLF': '日通し安値フラグ',
            'pDJ': '売買代金',
            'pAAV': '売数量（成行）',
            'pABV': '買数量（成行）',
            'pQOV': '売-OVER',
            'pGAV10': '売-１０-数量',
            'pGAP10': '売-１０-値段',
            'pGAV9': '売-９-数量',
            'pGAP9': '売-９-値段',
            'pGAV8': '売-８-数量',
            'pGAP8': '売-８-値段',
            'pGAV7': '売-７-数量',
            'pGAP7': '売-７-値段',
            'pGAV6': '売-６-数量',
            'pGAP6': '売-６-値段',
            'pGAV5': '売-５-数量',
            'pGAP5': '売-５-値段',
            'pGAV4': '売-４-数量',
            'pGAP4': '売-４-値段',
            'pGAV3': '売-３-数量',
            'pGAP3': '売-３-値段',
            'pGAV2': '売-２-数量',
            'pGAP2': '売-２-値段',
            'pGAV1': '売-１-数量',
            'pGAP1': '売-１-値段',
            'pGBV1': '買-１-数量',
            'pGBP1': '買-１-値段',
            'pGBV2': '買-２-数量',
            'pGBP2': '買-２-値段',
            'pGBV3': '買-３-数量',
            'pGBP3': '買-３-値段',
            'pGBV4': '買-４-数量',
            'pGBP4': '買-４-値段',
            'pGBV5': '買-５-数量',
            'pGBP5': '買-５-値段',
            'pGBV6': '買-６-数量',
            'pGBP6': '買-６-値段',
            'pGBV7': '買-７-数量',
            'pGBP7': '買-７-値段',
            'pGBV8': '買-８-数量',
            'pGBP8': '買-８-値段',
            'pGBV9': '買-９-数量',
            'pGBP9': '買-９-値段',
            'pGBV10': '買-１０-数量',
            'pGBP10': '買-１０-値段',
            'pQUV': '買-UNDER',
            'pVWAP': 'VWAP',
            'pPRP': '前日終値'}
        
        self.get_market_price = get_market_price.EntitySpec(self)
        # 以下、必要なエンドポイントを開発次第、追加していく
        # 今んとこは get_market_price だけ
    
    def req(self, url, params, headers=None):
        params['p_no'] = str(self.request_no)
        params['p_sd_date'] = dt.datetime.now().strftime('%Y.%m.%d-%H:%M:%S.%f')[:-3]
        params['sJsonOfmt'] = '5'
        params = json.dumps(params, ensure_ascii=False)
        url = f"{url}?{params}"
        res = requests.get(url, headers=headers)
        res.encoding = 'shift-jis'
        content = res.text
        self.request_no += 1
        return json.loads(content)

    def login(self):
        params = {
            'sCLMID': 'CLMAuthLoginRequest',
            'sUserId': self.user_id,
            'sPassword': self.password,
        }
        res = self.req(f"{self.base_url}/auth/", params)

        if int(res.get('p_errno', -1)) == 0 and int(res.get('sResultCode', -1)) == 0:
            if res.get('sUrlRequest'):
                self.session_info = {
                    'sUrlRequest': res.get('sUrlRequest'),
                    'sUrlMaster': res.get('sUrlMaster'),
                    'sUrlPrice': res.get('sUrlPrice'),
                    'sUrlEvent': res.get('sUrlEvent'),
                    'sZyoutoekiKazeiC': res.get('sZyoutoekiKazeiC')
                }
            else:
                raise Exception("契約締結前書面が未読です。ブラウザーで確認してください。")
        else:
            raise Exception(f"ログイン失敗: {res.get('p_err')}, コード: {res.get('sResultCode')}")

    def logout(self):
        if not self.session_info:
            return 
            
        params = {'sCLMID': 'CLMAuthLogoutRequest'}
        
        res = self.req(self.session_info['sUrlRequest'], params)
        if int(res.get('sResultCode', -1)) == 0:
            self.session_info = None
            return
        else:
            raise Exception(f"ログアウト失敗: {res.get('p_err')}, コード: {res.get('sResultCode')}")
        
    def code_to_name(self, code):
        return self.mapping.get(code, code)
```

```python ebranchapi/get_market_price.py
class EntitySpec(object):
    def __init__(self, ctx):
        self.ctx = ctx

    def __call__(self, **kwargs):
        symbols = kwargs.get("symbols")
        columns = kwargs.get("columns")
        params = {
            'sCLMID': 'CLMMfdsGetMarketPrice',
            'sTargetIssueCode': ','.join(symbols),
            'sTargetColumn': ','.join(columns),
        }
        res = self.ctx.req(
            self.ctx.session_info['sUrlPrice'],
            params)
        res = res.get('aCLMMfdsMarketPrice', [])
        return res
```

利用するときは、こんな感じ
```python
import ebranchapi
from common import const

api = ebranchapi.Context(
    base_url=const.BASE_URL, # 最後の/は含めない
    user_id=const.EBRANCH_USER_ID,
    password=const.EBRANCH_PASS,
)

api.login()

symbols = ["7203", "6758", "9984"]
columns = [ "pQAP", # 売気配値
            "pQBP", # 買気配値
            'pPRP', # 前日終値
            ]
data = api.get_market_price(symbols=symbols, columns=columns)

# => [
# {'sIssueCode': '7203', 'pQAP': '2720.0', 'pQBP': '2719.5', 'pPRP': '2365.5'},
# {'sIssueCode': '6758', 'pQAP': '3500', 'pQBP': '3499', 'pPRP': '3107'}, 
# {'sIssueCode': '9984', 'pQAP': '7070', 'pQBP': '7070', 'pPRP': '6070'}]

```