-
Notifications
You must be signed in to change notification settings - Fork 1
/
ibkr.lua
166 lines (149 loc) · 5.38 KB
/
ibkr.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
WebBanking {
version = 0.2,
country = "de",
description = "Include your IBKR stock portfolio in MoneyMoney.",
services = {"IBKR"}
}
local parseargs = function(s)
local arg = {}
string.gsub(s, "([%-%w]+)=([\"'])(.-)%2", function(w, _, value)
value = string.gsub(value, """, "\"");
value = string.gsub(value, "'", "'");
value = string.gsub(value, ">", ">");
value = string.gsub(value, "<", "<");
value = string.gsub(value, "&", "&");
arg[w] = value
end)
return arg
end
local parseBlock = function(content, k)
return string.match(content, "^.+<" .. k .. ">(.+)</" .. k .. ">.+$")
end
local connection = Connection()
local token
local query
local code
function SupportsBank(protocol, bankCode)
return protocol == ProtocolWebBanking and bankCode == "IBKR"
end
function InitializeSession(protocol, bankCode, username, customer, password)
token = password
query = username
connection = Connection()
local content, charset, mimeType = connection:get(
"https://gdcdyn.interactivebrokers.com/Universal/servlet/FlexStatementService.SendRequest?t=" .. token .. "&q=" ..
query .. "&v=3")
local status = string.match(content, "^.+<Status>(.+)</Status>.+$")
if status == "Success" then
code = string.match(content, "^.+<ReferenceCode>(.+)</ReferenceCode>.+$")
print("8:" .. code)
else
return content
end
end
function ListAccounts(knownAccounts)
local account = {
name = "IBKR",
accountNumber = 1,
currency = "EUR",
portfolio = true,
type = "AccountTypePortfolio"
}
local account2 = {
name = "IBKR Cash",
accountNumber = 2,
currency = "EUR",
type = "AccountTypeOther"
}
return {account, account2}
end
local statementContent
function stringToTimestamp(str)
local datePattern = '(%d%d%d%d)(%d%d)(%d%d)'
local year, month, day = str:match(datePattern)
if year and month and day then
local timestamp = os.time{day=day,month=month,year=year}
return timestamp
end
end
function RefreshAccount(account, since)
print("RefreshAccount " .. JSON():set(account):json())
if statementContent == nil then
local ec
repeat
statementContent, charset, mimeType = connection:get(
"https://gdcdyn.interactivebrokers.com/Universal/servlet/FlexStatementService.GetStatement?t=" .. token ..
"&q=" .. code .. "&v=3")
local ec=parseBlock(statementContent, 'ErrorCode')
if ec=="1019" then
MM.sleep(1)
end
until ec ~="1019"
end
if account.accountNumber == "1" then
local positions = parseBlock(statementContent, 'OpenPositions')
local securities = {}
for p in positions:gmatch("<OpenPosition(.-)/>") do
print(p)
local pos = parseargs(p)
securities[#securities + 1] = {
name = pos.description,
isin = pos.isin,
securityNumber = pos.isin,
market = pos.listingExchange,
quantity = pos.position * pos.multiplier,
originalCurrencyAmount = pos.positionValue,
currencyOfOriginalAmount = pos.currency,
price = pos.markPrice,
currencyOfPrice = pos.currency,
purchasePrice = pos.costBasisPrice,
currencyOfPurchasePrice = pos.currency,
exchangeRate = 1 / pos.fxRateToBase,
userdata = {{key="_profit",value=string.format("%.02f", pos.fifoPnlUnrealized*pos.fxRateToBase) .. " EUR / " .. string.format("%.05f", 100/pos.costBasisMoney*pos.positionValue-100) .. " %"}}
--userdata = {{key="_profit",value=string.format("%.02f", pos.fifoPnlUnrealized) .. " USD / " .. string.format("%.05f", 100/pos.costBasisMoney*pos.positionValue-100) .. " %"}}
}
end
-- Return balance and array of transactions.
return {
securities = securities
}
elseif account.accountNumber == "2" then
local summary = parseBlock(statementContent, 'EquitySummaryInBase')
local cash = 0
for p in summary:gmatch("<EquitySummaryByReportDateInBase(.-)/>") do
print(p)
local pos = parseargs(p)
cash = pos.cash
end
-- array of transactions.
local summary = parseBlock(statementContent, 'StmtFunds')
local transactions = {}
for p in summary:gmatch("<StatementOfFundsLine(.-)/>") do
--print(p)
local sm = parseargs(p)
print(#transactions,sm.transactionID,sm.reportDate,sm.settleDate,sm.description,sm.activityDescription,sm.amount,sm.activityCode)
if sm.activityCode ~= 'ADJ' then
transactions[#transactions + 1] = {
name=sm.description,
amount=sm.amount,
currency="EUR",
bookingDate=stringToTimestamp(sm.reportDate),
valueDate=stringToTimestamp(sm.settleDate),
transactionCode=sm.transactionID,
purpose=sm.activityDescription,
bookingText=sm.activityCode
}
end
end
-- Return balance and array of transactions.
--print(JSON():set(transactions):json())
return {
balance = cash,
transactions = transactions
}
end
end
function EndSession()
-- Logout.
end
-- SIGNATURE: MCsCFHF+25SfP/5FOEXYuH4H1XCoVDF7AhNF3D9StNKYUYIheUUOaFSh8dDr