Object-Oriented Programming (OOP) in Python 3
By: Michael Whittle
Tags: Python Object, Object Oriented, Object Model, Oop, Python3

I’m in the process of developing a crypto trading bot in Python. I want to create simulations to test various trading strategies on different markets and conditions to fine tune the bot. In order to do this I want to create an “Account” class object model to simulate a user account.

So what is a Class or Model?

At a basic level it is a template or blueprint for creating objects.

Let’s start with a very basic example…

I’m going to create a “blueprint” for creating “User” objects. I’m trying to keep this a simple as possible so not including any validation. Typically with an object you will have a “constructor”. This is the data included in the object as it is created.

You will then have what is commonly referred to as “getter” or “setter” functions. “getters” are functions you can call on the object to retrieve data from an instantiated object and “setters” are functions you can use to set data in the instantiated object. By “instantiated”, I mean the object instance created by the object “blueprint”.

The first step is we want to create a directory for our models. I just called mine “models”. In this directory we need to create an empty file called “__init__.py”.

.

|____models

| |______init__.py

The next step is to create the model. Please create a file called, “usermodel.py” in the “models” directory.

.

|____models

| |____usermodel.py

| |______init__.py

The content of, “usermodel.py” is as follows.

class User():

def __init__(self, name, age):

self.name = name

self.age = age def getName(self):

return self.name def getAge(self):

return self.age def setName(self, name):

self.name = name def setAge(self, age):

self.age = age

Hopefully this is self explanatory so far. The, “__init__” is your “constructor” as mentioned above. The functions starting with “get” are your “getters” and the functions starting with “set” are the “setters”. You can call these functions what ever you like. I personally just like to keep my “getters” and “setters” together and try where possible to keep the function names descriptive. Really these are just functions and you can call them what ever you like and you can add more or less functions. It’s really up to you.

Now create yourself another file called, “tutorial.py” in your project root with the following content.

from models.usermodel import User user1 = User('User1New', 20)

print (user1.getName())

print (user1.getAge()) user1.setName('User1Updated')

user1.setAge(21) print (user1.getName())

print (user1.getAge()) user2 = User('User2New', 30)

print (user2.getName())

print (user2.getAge())

This will return the following when you run it.

User1New

20

User1Updated

21

User2New

30

The first line will look in the “models” directory for the “usermodel” script and import the “User” class model “blueprint”.

You will see I created an object with the “name” set to “User1New” and the “age” set to 20 and stored it in the “user1” variable. I then used my two “getters” to retrieve the “name” and “age”. I then used my “setters” to update the “name” and “age” and the “getters” again to confirm the change.

I then created another object with the “name” set to “User2New” and “age” set to 30 and stored it in the “user2” variable. You will notice that the two objects created from the “blueprint” class model are completely independent instances.

It is probably worth briefly explaining “static” variables now as well. As you can see above each instantiated user object contains its own data and can be get and set individually. A “static” variable is a variable that is shared between all the objects created by the same class model “blueprint”.

Update the, “usermodel.py” to include a “static” variable called “count”.

class User():

count = 1

And update our “tutorial.py” with this.

from models.usermodel import User user1 = User('User1', 20)

print (User.count)

User.count = User.count + 1 user2 = User('User2', 30)

print (User.count)

As you can see you can access the static variable by calling the class, “User”.

1

2

Both “user1” and “user2” variables created from the same “blueprint” are sharing the same “count” static variable.

This is just a very brief example of what Object-Oriented Programming (OOP) is in Python 3. Hopefully it’s clear and you are following me so far.

As I mentioned in the beginning I am creating an, “Account” class for my crypto trading bot. It’s a work in progress but it looks like this so far.

from datetime import datetime class Account():

def __init__(self, user='anonymous'):

self.balance = 0

self.user = user

self.account = []

self.account.append([ datetime.now().strftime("%Y-%m-%d %H:%M:%S"), 0, 'open', 0, 0 ]) def getAccount(self):

return self.account def getBalance(self):

return self.balance def getUser(self):

return self.user def buy(self, quantity, price):

if not isinstance(quantity, float) and not isinstance(quantity, int):

raise TypeError('Quantity not numeric.') if not isinstance(price, float) and not isinstance(price, int):

raise TypeError('Price not numeric.') if quantity <= 0:

raise Exception('Insufficient quantity.') if price <= 0:

raise Exception('Invalid price.') if (quantity * price) > self.balance:

raise Exception('Insufficient funds.') self.balance = self.balance - (quantity * price)

self.account.append([ datetime.now().strftime("%Y-%m-%d %H:%M:%S"), quantity * price, 'buy', quantity, price ]) def deposit(self, amount):

if not isinstance(amount, float) and not isinstance(amount, int):

raise TypeError('Deposit amount not numeric.') if amount <= 0:

raise Exception('Insufficient deposit.') self.balance = self.balance + amount

self.account.append([datetime.now().strftime("%Y-%m-%d %H:%M:%S"), amount, 'deposit']) def sell(self, quantity, price):

if not isinstance(quantity, float) and not isinstance(quantity, int):

raise TypeError('Quantity not numeric.') if not isinstance(price, float) and not isinstance(price, int):

raise TypeError('Price not numeric.') if quantity <= 0:

raise Exception('Insufficient quantity.') if price <= 0:

raise Exception('Invalid price.') self.balance = self.balance + (quantity * price)

self.account.append([ datetime.now().strftime("%Y-%m-%d %H:%M:%S"), quantity * price, 'sell', quantity, price ]) def withdraw(self, amount):

if not isinstance(amount, float) and not isinstance(amount, int):

raise TypeError('Withdraw amount not numeric.') if amount <= 0:

raise Exception('Insufficient withdraw.') if amount > self.balance:

raise Exception('Insufficient funds.') self.balance = self.balance - amount

self.account.append([datetime.now().strftime("%Y-%m-%d %H:%M:%S"), -amount, 'withdraw'])

The class has a constructor to identity the “user”, the user “balance” and an “account” list of all the transactions. I have four functions to simulate a “deposit”, “withdraw”, “buy” and “sell”. This should be all I need for now for the crypto bot simulations.

You can try it out with this.

from models.account import Account account = Account() print(account.getUser())

print(account.getBalance()) account.deposit(100) print(account.getBalance()) account.withdraw(5) print(account.getBalance()) print (account.getAccount()) account.buy(10, 1)

account.sell(10, 1.5) print (account.getAccount()) print(account.getBalance())

With will print this.

anonymous

0

100

95

[['2020-12-07 10:13:28', 0, 'open', 0, 0],

['2020-12-07 10:13:28', 100, 'deposit'],

['2020-12-07 10:13:28', -5, 'withdraw']]

[['2020-12-07 10:13:28', 0, 'open', 0, 0],

['2020-12-07 10:13:28', 100, 'deposit'],

['2020-12-07 10:13:28', -5, 'withdraw'],

['2020-12-07 10:13:28', 10, 'buy', 10, 1],

['2020-12-07 10:13:28', 15.0, 'sell', 10, 1.5]]

100.0

The idea is to be able to run hundreds of simulations against different markets and conditions using various trading strategies. This “Account” object class will allow me to simulate users with an opening balance and all the activity. I will want to make sure that all my virtual user accounts have a positive balance before I start trading with real funds.

This doesn’t include trading exchange fees yet. I will add that later but I didn’t want to over complicate the process now.

I hope you learnt something from this tutorial and if you are interesting in the progress of this project of mine please remember to follow.

I have created a Git repository for this project which you can find here.

If you enjoyed reading this article and would like me to write on any other topics please let me know in the comments or email me directly.