-
Notifications
You must be signed in to change notification settings - Fork 1
/
globalToolsAssets.py
182 lines (161 loc) · 7.03 KB
/
globalToolsAssets.py
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
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
from globals import *
### HOW LEDGER SHARE BALANCES WORK ###
# Authorized shares on the ledger represent unrestricted stock when held outside of company accounts.
# Authorized_to_maintain_liabilities is used when an account is frozen due to government requirements.
# These shouldn't meaningfully affect float, and are currently marked as unrestricted/outstanding.
# Unauthorized shares are unrestricted stock held by a company insider who can't trade without help.
# Claimable balances are restricted shares.
# The claimable date indicates when the shares become unrestricted, typically following SEC Rule 144.
# If NOT unconditional predicate, the release date is denoted by offline case-by-case terms.
# Standard CB memo is "Basis: {basis}"
# Employee stock grants, options, and similar arrangements are planned through Soroban.
def getLedgerBalances(code):
return requestAWS(f"assets/{code}/balances")
def getNextLedgerData(links):
nextData = requests.get(
links["next"]["href"]
.replace("\u0026", "&")
.replace("%3A", ":")
).json()
return getLinksAndRecordsFromParsedLedger(nextData)
def getAllIssuerCompanyCodes():
allAssets = []
for addrs in BT_ISSUERS:
ledger = requestXLM(f"assets?asset_issuer={addrs}")
links, records = getLinksAndRecordsFromParsedLedger(ledger)
while(records):
for entries in records:
allAssets.append(entries["asset_code"])
links, records = getNextLedgerData(links)
return allAssets
def getNumRestrictedShares(queryAsset):
assetData = requestAssetRecords(queryAsset)
explicitRestrictedShares = Decimal(assetData["claimable_balances_amount"])
implicitRestrictedShares = Decimal("0")
for classifiers, balances in assetData["balances"].items():
if(classifiers != "authorized"):
implicitRestrictedShares += Decimal(balances)
return explicitRestrictedShares + implicitRestrictedShares
# todo: Change diction to reflect use of Soroban for options compensation data
# def getNumAuthorizedSharesGeneratedButNotOutstanding(companyCode, queryAsset):
def getNumAuthorizedSharesNotIssued(companyCode, queryAsset):
companyAccounts = [
"authorized.DSPP",
"initial.offering",
"reg.a.offering",
"reg.cf.offering",
"reg.d.offering",
"shelf.offering",
"reserved.employee",
"treasury"
]
shares = Decimal("0")
for accounts in companyAccounts:
holdingAccountPublicKey = resolveFederationAddress(f"{companyCode}*{accounts}.holdings")
shares += getCustodiedShares(queryAsset, holdingAccountPublicKey)
return shares
def getCustodiedShares(queryAsset, account):
if(not account): return 0
accountBalances = getLedgerBalancesForPublicKey(publicKey)
return getAssetBalanceFromAllBalances(queryAsset, accountBalances)
def getAffiliateShares(queryAsset): # TODO: rm, outdated
companyCode = getCompanyCodeFromAssetCode(queryAsset)
public = isPublic(companyCode)
if(not public):
affiliateAccount = requestAccount = resolveFederationAddress(f"{companyCode}*private.affiliate.holdings")
return getCustodiedShares(queryAsset, affiliateAccount)
else:
affiliateBalances = Decimal("0")
# fetch list of affiliates from accounts.toml
# get their balances
accounts = loadTomlData(BT_ACCOUNTS_TOML)
pprint(accounts)
return affiliateBalances
def getNumTreasuryShares(queryAsset):
treasuryAddr = resolveFederationAddress(f"{queryAsset}*treasury.holdings")
if(not treasuryAddr): return 0
accountBalances = getLedgerBalancesForPublicKey(treasuryAddr)
return getAssetBalanceFromAllBalances(queryAsset, accountBalances)
def getNumEmployeeBenefitShares(queryAsset):
employeeBenefitAddr = resolveFederationAddress(f"{queryAsset}*reserved.employee.holdings")
if(not employeeBenefitAddr): return 0
accountBalances = getLedgerBalancesForPublicKey(employeeBenefitAddr)
return getAssetBalanceFromAllBalances(queryAsset, accountBalances)
def getAssetBalanceFromAllBalances(queryAsset, accountBalances):
asset = getAssetObjFromCode(queryAsset)
for balances in accountBalances:
if(balances["asset_type"] != "native"):
searchAsset = Asset(balances["asset_code"], balances["asset_issuer"])
if(searchAsset == asset):
return balances["balance"]
def getLedgerBalancesForPublicKey(PK):
return requestXLM(f"accounts/{PK}")["balances"]
def getTransactionsForAsset(queryAsset):
# When SE payments relaunched:
# Get all transfers
# SEapiResponse = trySEpaymentsAPI
# Get all trades
# HorizonTradesSearch via horizon/trades?base_asset=DEMO...
# tmp: search investor txns for payments
# You need to iterate over all public keys to account for investors
# that previously transacted with the queryAsset but don't own now.
# use queryAsset DEMO to test transferSearching:
transactions = {}
allPublicKeys = getAllPublicKeys()
for addrs in allPublicKeys:
accountLinks = getAccountLinksDict(addrs)
paymentsLedger = getPaymentsLedgerFromAccountLinks(accountLinks)
paymentLinks, paymentRecords = getLinksAndRecordsFromParsedLedger(paymentsLedger)
while(paymentRecords):
for payments in paymentRecords:
if(
payments["type"] == "payment" and
payments["asset_type"] != "native" and
payments["asset_code"] == queryAsset and
payments["asset_issuer"] in BT_ISSUERS
):
transactions[
stripPagingNum(payments["paging_token"])
] = {
"type": "transfer",
"txHash": payments["transaction_hash"],
"amount": Decimal(payments["amount"]),
"from": payments["from"],
"to": payments["to"],
"timestamp": payments["created_at"],
}
paymentLinks, paymentRecords = getNextLedgerData(paymentLinks)
# use queryAsset ETH to test tradeSearching:
fiatAsset = USDC_ASSET # BT_DOLLAR
queryAsset = getAssetObjFromCode(queryAsset)
params = {
"base_asset_type": queryAsset.type,
"base_asset_code": queryAsset.code,
"base_asset_issuer": queryAsset.issuer,
"counter_asset_type": fiatAsset.type,
"counter_asset_code": fiatAsset.code,
"counter_asset_issuer": fiatAsset.issuer,
}
tradesLedger = requestXLM(f"trades", params)
try:
tradeLinks, tradeRecords = getLinksAndRecordsFromParsedLedger(tradesLedger)
except KeyError:
print(f"No trades found for {queryAsset.code} against {fiatAsset.code}")
return transactions
while(tradeRecords):
for trades in tradeRecords:
if(trades["trade_type"] != "liquidity_pool"): # add liquidity pools or path payments here
transactions[
stripPagingNum(trades["paging_token"])
] = {
"type": "trade",
"operationID": trades["id"].split("-")[0],
"buyer": trades["counter_account"],
"dollars": Decimal(trades["counter_amount"]),
"seller": trades["base_account"],
"shares": Decimal(trades["base_amount"]),
"price": divide(trades["price"]["n"], trades["price"]["d"]),
"timestamp": trades["ledger_close_time"]
}
tradeLinks, tradeRecords = getNextLedgerData(tradeLinks)
return transactions