盈透证券API持仓和打印订单python程序

盈透证券打印自己的持仓,未成交订单和已成交订单的python程序

import pandas as pd
from ib_insync import *
import datetime
import os
from pytz import timezone
from openpyxl import Workbook
from openpyxl.styles import Font
from openpyxl.utils import get_column_letter  # 导入 get_column_letter

# 辅助函数:生成期权完整名称
def get_contract_symbol(contract):
    if isinstance(contract, Option):
        symbol = contract.symbol
        expiry = contract.lastTradeDateOrContractMonth.replace('-', '')
        strike = str(contract.strike)
        right = contract.right[0]  # 'C' 或 'P'
        return f"{symbol} {expiry} {strike} {right}"
    else:
        return contract.symbol

# 连接到 IBKR
def connect_to_ibkr():
    ib = IB()
    ib.connect('127.0.0.1', 7496, clientId=0)
    print(f"已连接,clientId: 0")
    return ib

# 查询已成交订单(Fills)
def get_fills(ib):
    fills = ib.fills()
    return fills

# 处理Fill数据,合并同一订单,并转换为北京时间
def process_fills(fills):
    beijing_tz = timezone('Asia/Shanghai')
    orders = {}
    
    for fill in fills:
        order_key = fill.execution.permId if fill.execution.permId else fill.execution.orderId
        beijing_time = fill.time.astimezone(beijing_tz).strftime('%Y-%m-%d %H:%M:%S')
        
        if order_key not in orders:
            orders[order_key] = {
                'Symbol': get_contract_symbol(fill.contract),
                'Time': beijing_time,
                'Side': fill.execution.side,
                'Total Cost': 0.0,
                'Cumulative Qty': 0.0,
                'Avg Price': 0.0
            }
        
        order = orders[order_key]
        shares = float(fill.execution.shares)
        price = float(fill.execution.price)
        
        order['Total Cost'] += shares * price
        order['Cumulative Qty'] = float(fill.execution.cumQty)
        
        if beijing_time < order['Time']:
            order['Time'] = beijing_time
    
    for order in orders.values():
        if order['Cumulative Qty'] > 0:
            order['Avg Price'] = order['Total Cost'] / order['Cumulative Qty']
        del order['Total Cost']
    
    return list(orders.values())

# 主函数
def main():
    today = datetime.datetime.now().strftime('%Y%m%d_%H%M%S')
    output_file = f"C:/stock/python/ibkr_combined_{today}.xlsx"

    os.makedirs(os.path.dirname(output_file), exist_ok=True)

    ib = connect_to_ibkr()
    
    try:
        account_summary = ib.accountSummary()
        account_id = account_summary[0].account if account_summary else '未知'
        print(f"当前账户: {account_id}")

        # 1. 查询并处理已成交订单(Fills)
        fills = get_fills(ib)
        fills_data = process_fills(fills) if fills else []
        fills_df = pd.DataFrame(fills_data)
        if not fills_df.empty:
            fills_df = fills_df.sort_values(by=['Symbol', 'Time'], ascending=[True, False])
        print(f"已合并成交订单数量: {len(fills_data)} (原始成交记录数: {len(fills)})")

        # 2. 查询持仓并筛选价值超过 1000 USD 的股票,排除 USD.HKD
        positions = ib.portfolio()
        filtered_positions = []
        print(f"持仓数量: {len(positions)}")

        for pos in positions:
            contract = pos.contract
            local_symbol = contract.localSymbol if hasattr(contract, 'localSymbol') else f"{contract.symbol}.{contract.currency}"

            if local_symbol == 'USD.HKD':
                print(f"跳过 USD.HKD 持仓,不请求数据")
                continue

            if isinstance(contract, Forex):
                contract.exchange = 'IDEALPRO'
            elif not contract.exchange:
                contract.exchange = 'SMART'
            elif contract.primaryExchange == 'ISLAND':
                contract.exchange = 'NASDAQ'

            market_value = pos.marketValue
            if market_value > 1000:
                try:
                    ticker = ib.reqMktData(contract, '', True, False)
                    ib.sleep(1)
                    last_price = ticker.last if not util.isNan(ticker.last) else pos.marketPrice
                    filtered_positions.append({
                        'Symbol': get_contract_symbol(contract),
                        'Position': pos.position,
                        'Last Price': last_price,
                        'Market Value': market_value
                    })
                except Exception as e:
                    print(f"请求 {get_contract_symbol(contract)} 数据时出错: {e}")
                    continue

        positions_df = pd.DataFrame(filtered_positions)

        # 3. 查询所有交易数据(仅提取未成交订单)
        all_trades = ib.trades()
        ib.reqAllOpenOrders()
        ib.sleep(5)

        pending_from_trades = []
        print(f"所有交易数量: {len(all_trades)}")
        
        for trade in all_trades:
            order = trade.order
            order_id = order.orderId
            if order_id < 0:
                contract = trade.contract
                pending_from_trades.append({
                    'Symbol': get_contract_symbol(contract),
                    'Action': order.action,
                    'Quantity': order.totalQuantity,
                    'Lmt Price': order.lmtPrice if hasattr(order, 'lmtPrice') else None,
                })

        pendingtrades_df = pd.DataFrame(pending_from_trades)
        if not pendingtrades_df.empty:
            pendingtrades_df = pendingtrades_df.sort_values(by='Symbol')
        print(f"未成交订单数量(从 trades 提取,负 Order ID): {len(pending_from_trades)}")

        # 写入 Excel 文件并设置工作表颜色
        with pd.ExcelWriter(output_file, engine='openpyxl') as writer:
            positions_df.to_excel(writer, sheet_name='Positions Over 1000 USD', index=False)
            pendingtrades_df.to_excel(writer, sheet_name='Pending Orders', index=False)
            fills_df.to_excel(writer, sheet_name='Executed Fills', index=False)

            # 获取工作簿对象
            workbook = writer.book

            # 设置 'Positions Over 1000 USD' 为金色
            positions_sheet = workbook['Positions Over 1000 USD']
            positions_sheet.sheet_properties.tabColor = 'B8860B'  # 金色标签
            max_col = get_column_letter(positions_sheet.max_column)  # 获取最大列字母
            for row in positions_sheet[f'A1:{max_col}{positions_sheet.max_row}']:
                for cell in row:
                    cell.font = Font(color='B8860B')  # 金色文字
            print("已将 'Positions Over 1000 USD' 设置为金色")

            # 设置 'Pending Orders' 为红色
            pending_sheet = workbook['Pending Orders']
            pending_sheet.sheet_properties.tabColor = 'FF0000'  # 红色标签
            max_col = get_column_letter(pending_sheet.max_column)  # 获取最大列字母
            for row in pending_sheet[f'A1:{max_col}{pending_sheet.max_row}']:
                for cell in row:
                    cell.font = Font(color='FF0000')  # 红色文字
            print("已将 'Pending Orders' 设置为红色")

            # 设置 'Executed Fills' 为蓝色
            fills_sheet = workbook['Executed Fills']
            fills_sheet.sheet_properties.tabColor = '0000FF'  # 蓝色标签
            max_col = get_column_letter(fills_sheet.max_column)  # 获取最大列字母
            for row in fills_sheet[f'A1:{max_col}{fills_sheet.max_row}']:
                for cell in row:
                    cell.font = Font(color='0000FF')  # 蓝色文字
            print("已将 'Executed Fills' 设置为蓝色")

        print(f"数据已保存至 {output_file}")

    except Exception as e:
        print(f"发生错误: {e}")

    finally:
        ib.disconnect()

if __name__ == "__main__":
    main()
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值