Skip to content

Commit

Permalink
0.1.1
Browse files Browse the repository at this point in the history
  • Loading branch information
harrytwigg committed Feb 18, 2021
1 parent fde7cdf commit 2e2a047
Show file tree
Hide file tree
Showing 8 changed files with 414 additions and 16 deletions.
2 changes: 2 additions & 0 deletions MANIFEST
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
setup.cfg
setup.py
viaduct\__init__.py
viaduct\cfd.py
viaduct\core.py
viaduct\equity.py
viaduct\installer.py
viaduct\installerUtils.py
viaduct\isa.py
Expand Down
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ pip install viaduct

## Import and Usage

Viaduct is an API wrapper. API responses are returned python dictionaries, see the root tree images for more information. Selenium web scrapping modes extend the public class so it does not need to be recreated.
Viaduct is an API wrapper. API responses are returned python dictionaries, see the root tree images for more information. Selenium web scrapping modes extend the public class methods so it does not need to be recreated.

Examples are the payloads that the Rest API returns, these are returned as Python dictionaries for your convenience

Expand Down Expand Up @@ -64,7 +64,7 @@ Selenium powered wrapper for management of an ISA account, real mode only
If Firefox is not installed in default folder, make sure you pass the installed path upon initialisation:

```python
instance = ISA("email", "password", browserPath=r"C:\Program Files\Mozilla Firefox\firefox.exe")
instance = ISA("email", "password", browserPath=r"C:\Program Files\Mozilla Firefox\firefox.exe", loadSymbols=False)
```

## Equity Mode
Expand All @@ -74,7 +74,7 @@ Selenium powered wrapper for management of an Equity account, real or demo modes
If Firefox is not installed in default folder, make sure you pass the installed path upon initialisation:

```python
instance = Equity("email", "password", reality=Reality.Real, browserPath=r"C:\Program Files\Mozilla Firefox\firefox.exe")
instance = Equity("email", "password", reality=Reality.Real, browserPath=r"C:\Program Files\Mozilla Firefox\firefox.exe", loadSymbols=False)
```

## CFD Mode
Expand All @@ -86,7 +86,7 @@ Custom methods for CFD are not yet supported and only return Public API calls
If Firefox is not installed in default folder, make sure you pass the installed path upon initialisation:

```python
instance = CFD("email", "password", reality=Reality.Real, browserPath=r"C:\Program Files\Mozilla Firefox\firefox.exe")
instance = CFD("email", "password", reality=Reality.Real, browserPath=r"C:\Program Files\Mozilla Firefox\firefox.exe", loadSymbols=False)
```

## API Class Methods
Expand Down
Binary file removed dist/viaduct-0.0.12.tar.gz
Binary file not shown.
144 changes: 144 additions & 0 deletions lib/equityISAShared.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
# Format url on whether is live
def urlf(self, url):
if (self.reality == Reality.Real):
return ("https://live.trading212.com" + url)
elif (self.reality == Reality.Practice):
return ("https://demo.trading212.com" + url)
else:
raise Exception("Invalid reality " + self.reality)

# Gets min and max buy and sell value in £, max sell quantity is number of shares
# Not sure what sellThreshold is
# The sell parameters only appear for UK stocks for some reason
def getMinMax(self, code):
return get(self.urlf("/rest/v1/equity/value-order/min-max?instrumentCode=" + code), cookies=self.cookiePayload)

# Takes instrument code, returns maxBuy and maxSell in shares for the account and max buy and sell that is technically
# possible on the exchange, also has minTrade and if suspended
def getSettings(self, code):
return post(self.urlf("/rest/v2/account/instruments/settings"), cookies=self.cookiePayload, payload=[code])

# Gets the account performance graph data
def getPortfolioPerformance(self, historyPeriod):
return get(self.urlf("/rest/v2/portfolio?period=" + historyPeriod), cookies=self.cookiePayload)

# Get all instruments on Trading212!
def getAllInstruments(self):
return get(self.urlf("/rest/v2/instruments/"))

# Gets company ISINs and tickers for graphs etc
def getTickers(self):
return get(self.urlf("/rest/companies"))

# Saves all instruments in a dictionary useful for symbol lookup
def saveAllInstruments(self):
logging.info("Loading all instruments, this will take a while...")
self.instruments = self.getAllInstruments()
# Don't think getting tickers is actually required
#self.tickerISINs = self.getTickers()
self.loadedInstruments = True

# Search the instrument dictionary
def instrumentSearch(self, searchUsing, searchKey, resultUsing):
if (not self.loadedInstruments):
self.saveAllInstruments()
for i in self.instruments:
try:
if (i[searchUsing] == searchKey):
return i[resultUsing]
except:
pass

# Gets a symbols name
def getName(self, isin="", prettyName="", code="", id=""):
if (isin != ""):
return self.instrumentSearch("isin", isin, "name")
if (prettyName != ""):
return self.instrumentSearch("prettyName", prettyName, "name")
if (code != ""):
return self.instrumentSearch("code", code, "name")
if (id != ""):
return self.instrumentSearch("id", id, "name")
else:
raise Exception(
"ISIN, prettyName, code, or id is required to find name")

# Gets a symbols prettyName
def getPrettyName(self, isin="", name="", code="", id=""):
if (isin != ""):
return self.instrumentSearch("isin", isin, "prettyName")
if (name != ""):
return self.instrumentSearch("name", name, "prettyName")
if (code != ""):
return self.instrumentSearch("code", code, "prettyName")
if (id != ""):
return self.instrumentSearch("id", id, "prettyName")
else:
raise Exception(
"ISIN, name, code, or id is required to find prettyName")

# Gets a symbols code
def getCode(self, isin="", name="", prettyName="", id=""):
if (isin != ""):
return self.instrumentSearch("isin", isin, "code")
if (name != ""):
return self.instrumentSearch("name", name, "code")
if (prettyName != ""):
return self.instrumentSearch("prettyName", prettyName, "code")
if (id != ""):
return self.instrumentSearch("id", id, "code")
else:
raise Exception(
"ISIN, name, prettyName, or id is required to find code")

# Gets a symbols ISIN
def getISIN(self, name="", prettyName="", code="", id=""):
if (name != ""):
return self.instrumentSearch("name", name, "isin")
if (prettyName != ""):
return self.instrumentSearch("prettyName", prettyName, "isin")
if (code != ""):
return self.instrumentSearch("code", code, "isin")
if (id != ""):
return self.instrumentSearch("id", id, "isin")
else:
raise Exception(
"Name, prettyName, code, or id is required to find ISIN")

# Gets a symbols ID
def getID(self, isin="", name="", prettyName="", code=""):
if (isin != ""):
return self.instrumentSearch("isin", isin, "id")
if (name != ""):
return self.instrumentSearch("name", name, "id")
if (prettyName != ""):
return self.instrumentSearch("prettyName", prettyName, "id")
if (code != ""):
return self.instrumentSearch("code", code, "id")
else:
raise Exception(
"ISIN, name, prettyName, or code is required to find id")

# Get instrument details position code from the secret API
def getInstrument(self, code):
return get(self.urlf("/rest/v2/instruments/" + code))

# Get instrument details by ISIN
# If language is not available, Trading212 seems to return English
def getFundamentals(self, isin, langCode="en"):
return get(self.urlf("/rest/companies/fundamentals?languageCode=" + langCode + "&isin=" + isin))

# Gets chart data for a particular ticker
# When getting chart data, the ticker returned is the code not what is on the stock exchange!
def getChartData(self, code, chartPeriod, size, includeFake=False):
payload = {
"candles": [
{
"ticker": code,
"period": chartPeriod,
"size": size,
"includeFake": includeFake
}
]
}
return post(url=self.urlf("/charting/v2/batch"), payload=payload)
4 changes: 2 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
setup(
name='viaduct', # How you named your package folder (MyLib)
packages=['viaduct'], # Chose the same as "name"
version='0.1.0', # Start with a small number and increase it with every change you make
version='0.1.1', # Start with a small number and increase it with every change you make
# Chose a license from here: https://help.github.com/articles/licensing-a-repository
license='gpl-3.0',
# Give a short description about your library
Expand All @@ -14,7 +14,7 @@
# Provide either the link to your github or to your website
url='https://github.com/harrytwigg/Viaduct-Trading212-Python-API',
# I explain this later on
download_url='https://github.com/harrytwigg/Viaduct-Trading212-Python-API/archive/0.1.0.tar.gz',
download_url='https://github.com/harrytwigg/Viaduct-Trading212-Python-API/archive/0.1.1.tar.gz',
keywords=['python', 'api', 'rest', 'api-wrapper', 'viaduct', 'trading212',
'trading212-api'], # Keywords that define your package best
install_requires=[ # I get to this in a second
Expand Down
2 changes: 1 addition & 1 deletion viaduct/cfd.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@


class CFD(CoreModule):
def __init__(self, email, password, reality, browserPath, headless=False, timeout=2):
def __init__(self, email, password, reality, browserPath, headless=False, timeout=2, loadSymbols=False):
super().__init__(email, password, TradingType.CFD,
reality, headless, browserPath, timeout)
self.reality = reality
Expand Down
136 changes: 131 additions & 5 deletions viaduct/equity.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,15 @@


class Equity(CoreModule):
def __init__(self, email, password, reality, browserPath, headless=False, timeout=2):
def __init__(self, email, password, reality, browserPath, headless=False, timeout=2, loadSymbols=False):
super().__init__(email, password, TradingType.Equity,
reality, headless, browserPath, timeout)
self.reality = reality
self.reality = Reality.Real

if (loadSymbols):
self.saveAllInstruments()
else:
self.loadedInstruments = False

# Format url on whether is live
def urlf(self, url):
Expand All @@ -21,13 +26,134 @@ def urlf(self, url):
# Not sure what sellThreshold is
# The sell parameters only appear for UK stocks for some reason
def getMinMax(self, code):
return super().get(self.urlf("/rest/v1/equity/value-order/min-max?instrumentCode=" + code), cookies=self.cookiePayload)
return get(self.urlf("/rest/v1/equity/value-order/min-max?instrumentCode=" + code), cookies=self.cookiePayload)

# Takes instrument code, returns maxBuy and maxSell in shares for the account and max buy and sell that is technically
# possible on the exchange, also has minTrade and if suspended
def getSettings(self, code):
return super().post(self.urlf("/rest/v2/account/instruments/settings"), cookies=self.cookiePayload, payload=[code])
return post(self.urlf("/rest/v2/account/instruments/settings"), cookies=self.cookiePayload, payload=[code])

# Gets the account performance graph data
def getPortfolioPerformance(self, historyPeriod):
return super().get(self.urlf("/rest/v2/portfolio?period=" + historyPeriod), cookies=self.cookiePayload)
return get(self.urlf("/rest/v2/portfolio?period=" + historyPeriod), cookies=self.cookiePayload)

# Get all instruments on Trading212!
def getAllInstruments(self):
return get(self.urlf("/rest/v2/instruments/"))

# Gets company ISINs and tickers for graphs etc
def getTickers(self):
return get(self.urlf("/rest/companies"))

# Saves all instruments in a dictionary useful for symbol lookup
def saveAllInstruments(self):
logging.info("Loading all instruments, this will take a while...")
self.instruments = self.getAllInstruments()
# Don't think getting tickers is actually required
#self.tickerISINs = self.getTickers()
self.loadedInstruments = True

# Search the instrument dictionary
def instrumentSearch(self, searchUsing, searchKey, resultUsing):
if (not self.loadedInstruments):
self.saveAllInstruments()
for i in self.instruments:
try:
if (i[searchUsing] == searchKey):
return i[resultUsing]
except:
pass

# Gets a symbols name
def getName(self, isin="", prettyName="", code="", id=""):
if (isin != ""):
return self.instrumentSearch("isin", isin, "name")
if (prettyName != ""):
return self.instrumentSearch("prettyName", prettyName, "name")
if (code != ""):
return self.instrumentSearch("code", code, "name")
if (id != ""):
return self.instrumentSearch("id", id, "name")
else:
raise Exception(
"ISIN, prettyName, code, or id is required to find name")

# Gets a symbols prettyName
def getPrettyName(self, isin="", name="", code="", id=""):
if (isin != ""):
return self.instrumentSearch("isin", isin, "prettyName")
if (name != ""):
return self.instrumentSearch("name", name, "prettyName")
if (code != ""):
return self.instrumentSearch("code", code, "prettyName")
if (id != ""):
return self.instrumentSearch("id", id, "prettyName")
else:
raise Exception(
"ISIN, name, code, or id is required to find prettyName")

# Gets a symbols code
def getCode(self, isin="", name="", prettyName="", id=""):
if (isin != ""):
return self.instrumentSearch("isin", isin, "code")
if (name != ""):
return self.instrumentSearch("name", name, "code")
if (prettyName != ""):
return self.instrumentSearch("prettyName", prettyName, "code")
if (id != ""):
return self.instrumentSearch("id", id, "code")
else:
raise Exception(
"ISIN, name, prettyName, or id is required to find code")

# Gets a symbols ISIN
def getISIN(self, name="", prettyName="", code="", id=""):
if (name != ""):
return self.instrumentSearch("name", name, "isin")
if (prettyName != ""):
return self.instrumentSearch("prettyName", prettyName, "isin")
if (code != ""):
return self.instrumentSearch("code", code, "isin")
if (id != ""):
return self.instrumentSearch("id", id, "isin")
else:
raise Exception(
"Name, prettyName, code, or id is required to find ISIN")

# Gets a symbols ID
def getID(self, isin="", name="", prettyName="", code=""):
if (isin != ""):
return self.instrumentSearch("isin", isin, "id")
if (name != ""):
return self.instrumentSearch("name", name, "id")
if (prettyName != ""):
return self.instrumentSearch("prettyName", prettyName, "id")
if (code != ""):
return self.instrumentSearch("code", code, "id")
else:
raise Exception(
"ISIN, name, prettyName, or code is required to find id")

# Get instrument details position code from the secret API
def getInstrument(self, code):
return get(self.urlf("/rest/v2/instruments/" + code))

# Get instrument details by ISIN
# If language is not available, Trading212 seems to return English
def getFundamentals(self, isin, langCode="en"):
return get(self.urlf("/rest/companies/fundamentals?languageCode=" + langCode + "&isin=" + isin))

# Gets chart data for a particular ticker
# When getting chart data, the ticker returned is the code not what is on the stock exchange!
def getChartData(self, code, chartPeriod, size, includeFake=False):
payload = {
"candles": [
{
"ticker": code,
"period": chartPeriod,
"size": size,
"includeFake": includeFake
}
]
}
return post(url=self.urlf("/charting/v2/batch"), payload=payload)
Loading

0 comments on commit 2e2a047

Please sign in to comment.