Python乾貨分享:Python面向對象編程入門及股票數據管理應用實例

1 前言

一般而言,在學習或練習python的初級階段,我們在Jupyter Notebook(spyder或pycharm)上進行逐條執行語句和代碼,這樣可以起到交互的良好效果。但是如果要進行大一點的項目實踐,這種毫無規劃的逐條執行語句與指令就顯得不太適用了。為了使代碼得到最大程度的重複使用,並且各模塊之間邏輯更清晰,這時我們就有必要去學習模塊化的抽象設計了。模塊化的抽象設計基本思路是把主要框架和算法流程描述出來,再補充相應的細節。面向對象編程(Object-Oriented Programming)是模塊化設計的重要方法之一,是Python進階學習的必經之路。

本文主要介紹了面向對象編程的基礎知識,並以股票數據管理為例,介紹面向對象編程的具體應用。後續推文將會以更復雜的形式呈現面向對象編程在實現股票策略量化回測系統中的應用。

Python乾貨分享:Python面向對象編程入門及股票數據管理應用實例

2 class類編程入門

01class類的定義

類是具有相同屬性與方法的對象集合。

python定義類的語法有兩種:

class 類名稱:

class 類名稱(繼承類名稱): def 定義屬性和方法

class後面是類名稱,小括號裡的繼承類名稱表示定義的類繼承自哪一類。如果沒有繼承,則填object。比如下面定義一個股票stock類,屬性和方法先不寫,使用pass(跳過)表示。

<code>#定義一個股票的類
class stock(object):
pass
#創建了stock這個類之後,便可以使用“stock()”來創建實例
s=stock()
s
#輸出結果:<__main__.stock at=""/>/<code>

s是一個創建自stock類的實例。Python是一種動態語言,能夠動態地綁定一個實例的屬性與方法,如給s綁定一個code的代碼屬性:

<code>s.code='000001.SZ' 

s.code
#輸出結果:'000001.SZ'/<code>

同理,也可以從stock類創建一個s1的實例,並綁定代表價格的屬性:

<code>s1=stock()
s1.price=15.8
s1.price
#輸出結果: 15.8/<code>

通過動態綁定可以擁有不同的屬性,但是這樣不利於項目的開發與維護。若希望創建自同一個類的實例擁有一些共同的屬性,則可以通過定義一個特殊的函數__init__方法(注意下劃線是在英文語境下輸入兩次),來綁定在創建實例時非填不可的屬性。

__init__()第一個參數根據慣例為self,用於代指被實例化出來的對象,參數code和price是用於給對象的屬性賦值,比如一個股票的類假設有代碼和價格兩個屬性,則定義stock的類可以寫成:

<code>class stock(object):
def __init__(self,code,price):
self.code=code
self.price=price/<code>

python通過遵循一定的屬性和方法命名規則來達到訪問控制的效果:

  • 以單下劃線開頭的名字代表protect(受保護的),如self._price
  • 以雙下劃線開頭的名字代表private,如self.__init_change(self):
  • 以雙下劃線開頭結尾的名字代表系統保留定義,如__init__(self)、__str__(self)、__len__(self)

創建實例時,__init__方法中的參數,除了self不用輸入外,其他參數非填不可,否則會報錯。

在創建完類的名稱後,可以加入一些文字說明,簡要闡述類的主要內容,以方便使用者快速瞭解該類的功能,這些註釋存儲成字符串的形式。注意python註釋的寫法,單行註釋前面加“#”,多行註釋使用三引號。

<code>class stock(object):
'''
stock類中包含屬性code和price
'''
def __init__(self,code,price):
self.code=code
self.price=price
#查看類中的註釋內容
print(stock.__doc__)
輸出結果:stock類中包含屬性code和price/<code>

假如將上述代碼保存為StockClass.py本地文件(可以使用Anaconda的Spyder來寫腳本程序),保存在jupyter當前運行路徑中,則可以通過導入類名的方式來使用類。

<code>from StockClass import stock
print(stock.__doc__)/<code>

02類的調用方法

類的調用方法主要有三種:(1)類的實例;(2)靜態方法(@裝飾器);(3)類的方法(clc)。

實例調用最常見,一般使用“類名.方法”。靜態方法由類調用,無默認參數。將實例方法參數中的self去掉,然後在方法定義上方加上@staticmethod(前面加@是python函數的裝飾器方法),就成為靜態方法。類方法由類調用,採用@classmethod裝飾,至少傳入一個cls(代指類本身,類似self)參數。執行類方法時,自動將調用該方法的類賦值給cls,建議使用類名.類方法的調用方式。

實例調用法:

<code>class StockCode:
def __init__(self,name,code):
self.name=name
self.code=code
def get_stock(self):
return (self.name,self.code)
s=StockCode('中國平安','601318.SH')
s.get_stock()
#輸出結果:('中國平安', '601318.SH')/<code>

靜態方法調用

<code>class Codes(object):
@staticmethod
def get_code(s):
if len(s)==6:
return (s+'SH') if s.startswith('6') else (s+'SZ')
else:
print('股票代碼必須為6位數字!')

Codes.get_code('00001')
Codes.get_code('000001')
#輸出結果:股票代碼必須為6位數字!
#'000001SZ'/<code>

類調用法:

有的時候傳入的參數並不是('中國平安','601318.SH')這樣的格式,而是('中國平安-601318.SH')這樣的,那該怎麼做?首先要把這個拆分,但是要使用實例方法實現起來很麻煩,這個時候就可以使用類方法。

<code>class StockCode:
def __init__(self,stock,code):
self.stock=stock
self.code=code
@classmethod
# 裝飾器,立馬執行下面的函數
def split(cls,sc):
# cls是默認的這個類的init函數,sc是傳入參數
stock,code=map(str,sc.split('-'))
# 這裡轉換成了格式化的結構
dd = cls(stock,code)
# 然後執行這個類第一個方法
return dd
s=StockCode.split(('中國平安-601318.SH'))
#查看屬性
s.stock,s.code
#輸出結果:('中國平安', '601318.SH')
/<code>


03類的三大特徵:封裝、繼承與多態

類有三個特徵,分別是封裝、繼承和多態。封裝簡單理解是,將屬性與方法放在某個對象內部,使外部無法訪問。接著之前的例子,若要打印股票代碼code和price,則可以先定義一個打印的函數,對之前的類修改如下:

<code>class stock(object):
def __init__(self,code,price):
self.code=code
self.price=price
#定義打印屬性的函數
def print_attr(self):
print(f'股票代碼為:{self.code}')
print(f'股票價格為:{self.price}')
#使用“實例.方法”,即“實例名稱.函數名()”的方式來調用
s1=stock('000001.SZ',15.8)
s1.print_attr()
#輸出結果: 股票代碼為:000001.SZ
#股票價格為:15.8/<code>

把方法寫在對象內部,仍無法防止對象的屬性被無關的函數意外改變或錯誤使用。為了對屬性提供更加安全的保障,可以限制它們不被外界訪問,可以在屬性變量前面加上兩個劃線表示private屬性,使其屬性只能在類的內部進行訪問,這時通過“實例.屬性名稱”從外部訪問就會報錯。通過priate屬性訪問限制,使對象內部狀態得到保護,但若需要獲取private屬性,可以在類的內部編寫一個獲取屬性的方法。

<code>class stock(object):
def __init__(self,code,price):
self.__code=code
self.__price=price
def get_attr(self):
return(self.__code,self.__price)
s1=stock('000001.SZ',15.8)
s1.get_attr()
#輸出結果:('000001.SZ', 15.8)/<code>

與從外部直接訪問屬性的設計相比,把方法封裝在類中的好處是可以賦予方法一些行為規範。比如,若stock類需要修改code值的功能,則可以在類的內部增加一個set_code()的方法,且在定義該方法時,規定傳入的值必須為字符串類型,否則報錯。

<code>class stock(object):
def __init__(self,code,price):
self.__code=code
self.__price=price
def get_attr(self):
return(self.__code,self.__price)
def set_code(self,codevalue):
if type(codevalue)!=str:
return("錯誤,輸入參數必須為字符型")
self.__code=codevalue
s1=stock('000001.SZ',15.8)
print(s1.set_code(60000))
#重新定義是s1的代碼
s1.set_code('600000.SH')
print(s1.get_attr())
#輸出結果:
錯誤,輸入參數必須為字符型
('600000.SH', 15.8)/<code>

繼承是充分利用已有的類功能,在其基礎上進行擴展和定義新的類。繼承概念的實現方式主要有2類:實現繼承、接口繼承。實現繼承是指使用基類的屬性和方法而無需額外編碼的能力。接口繼承是指僅使用屬性和方法的名稱、但是子類必須提供實現的能力(子類重構父類方法)。在使用python編寫量化回測系統時經常會用到class類的繼承功能。

<code>#實現繼承
class Strategy(object):
def print_info(self):
print('策略模塊的功能是生成“多”或“空”的交易信號')
class my_strategy(Strategy):
pass
ss=my_strategy()
ss.print_info()
#輸出結果:
策略模塊的功能是生成“多”或“空”的交易信號/<code>

繼承有什麼好處?最大的好處是子類獲得了父類的全部功能。由於Strategy實現了print_info()方法,因此,my_strategy作為它的子類,就自動擁有了print_info()方法。

<code>#接口繼承
#這裡需要先引入一個抽象模塊abc
from abc import ABCMeta, abstractmethod
#python中使用@表示裝飾器的意思,具體可以參照python裝飾器的相關教程
class Strategy(object):
"""Strategy是一個抽象基類"""
# abc.ABCMeta是實現抽象類的一個基礎類
__metaclass__ = ABCMeta
#子類必須有這個方法,否則報錯
@abstractmethod # 定義抽象方法,無需實現功能
def generate_signals(self):
"""輸入數據產生多空的交易信號"""
raise NotImplementedError("應包含方法:
generate_signals()!")

#定義子類
class my_strategy2(Strategy):
def generate_signals(self):
pass

ss2.generate_signals()
#輸出結果:實現交易信號功能

ss1.generate_signals()/<code>

儘管上面的接口很簡單,但是當為每種特定類型的策略繼承此類時,它將變得更加複雜。策略類的目標是為投資組合模塊中提供多/空/持有信號。繼承可以把父類的所有功能都直接拿過來,這樣就不必重零做起,子類只需要新增自己特有的方法,也可以把父類不適合的方法覆蓋重寫;有了繼承,才能有多態。多態的意思是,當子類(strategy2)與父類(Strategy)具有相同的方法(print_info())時,在實例子類的時候會自動調用子類的該方法(而不是父類的方法)。

<code>class Strategy(object):
def print_info(self):
print('策略模塊的功能是生成“多”或“空”的交易信號')
#繼承
class strategy1(Strategy):
pass
#多態
class strategy2(Strategy):
def print_info(self):
print('這是個人的交易策略')
#實例化
strategy1().print_info()
strategy2().print_info()
#輸出結果:

策略模塊的功能是生成“多”或“空”的交易信號
這是個人的交易策略/<code>

3 class類編程在金融量化中的應用

應用背景:使用tushare獲取所有A股每日交易數據,保存到本地數據庫,同時每日更新數據庫;根據行情數據進行可視化和簡單的策略分析與回測。由於篇幅有限,本文著重介紹股票數據管理(下載、數據更新)的面向對象編程應用實例。

<code>#導入需要用到的模塊
import numpy as np
import pandas as pd
from dateutil.parser import parse
from datetime import datetime,timedelta
#操作數據庫的第三方包,使用前先安裝pip install sqlalchemy
from sqlalchemy import create_engine
#tushare包設置
import tushare as ts
token='輸入你在tushare上獲得的token'
pro=ts.pro_api(token)

#使用python3自帶的sqlite數據庫
#本人創建的數據庫地址為c:\\zjy\\db_stock\\
file='sqlite:///c:\\\\zjy\\\\db_stock\\\\'
#數據庫名稱
db_name='stock_data.db'
engine = create_engine(file+db_name)
class Data(object):
def __init__(self,
start='20050101',
end='20191115',
table_name='daily_data'):

self.start=start
self.end=end
self.table_name=table_name
self.codes=self.get_code()
self.cals=self.get_cals()
#獲取股票代碼列表
def get_code(self):
codes = pro.stock_basic(list_status='L').ts_code.values
return codes
#獲取股票交易日曆
def get_cals(self):
#獲取交易日曆
cals=pro.trade_cal(exchange='')
cals=cals[cals.is_open==1].cal_date.values
return cals
#每日行情數據
def daily_data(self,code):
try:
df0=pro.daily(ts_code=code,start_date=self.start,
end_date=self.end)
df1=pro.adj_factor(ts_code=code,trade_date='')
#復權因子
df=pd.merge(df0,df1) #合併數據
except Exception as e:
print(code)
print(e)
return df
#保存數據到數據庫
def save_sql(self):
for code in self.codes:
data=self.daily_data(code)
data.to_sql(self.table_name,engine,
index=False,if_exists='append')
#獲取最新交易日期
def get_trade_date(self):
#獲取當天日期時間
pass
#更新數據庫數據
def update_sql(self):
pass #代碼省略
#查詢數據庫信息
def info_sql(self):
pass #代碼省略/<code>

代碼運行

<code>#假設你將上述代碼封裝成class Data
#保存在'C:\\zjy\\db_stock'目錄下的down_data.py中
import sys
#添加到當前工作路徑
sys.path.append(r'C:\\zjy\\db_stock')

#導入py文件中的Data類
from download_data import Data
#實例類
data=Data()
#data.save_sql() #只需運行一次即可
data.update_sql()
data.info_sql() /<code>

輸出結果:數據已經是最新的!統計查詢的總數:7747184數據期間:20050104——20191125數據庫包含股票個數:3724

<code>#另外又根據畫圖需要,從數據庫中提取數據畫K線圖
#寫了一個stock_plot類保存在plot_stock.py文件中
from plot_stock import stock_plot
shfz=stock_plot('雙匯發展')
shfz.kline_plot() #普通K線圖/<code>
Python乾貨分享:Python面向對象編程入門及股票數據管理應用實例

<code>#畫修正版K線圖
shfz.kline_plot(ktype=1)/<code>
Python乾貨分享:Python面向對象編程入門及股票數據管理應用實例

4 結語

本文主要介紹了面向對象編程的基礎知識和股票數據管理應用實例。

通常我們會以各種數據信息來刻畫描述一個對象,而這個對象包含某些屬性或特徵。比如,將某隻股票視為一個對象,那麼該股票對象包含的信息可能有股票代碼、公司名稱、所屬行業、收盤價等。這些用以描述對象特徵的數據信息稱為對象的屬性(Attribute);而存取屬性的函數則稱為方法(Method),是該對象與外界溝通的接口。具有相同屬性與方法的對象構成了一個類別(Class)。換句話說,類是一種將對象抽象化而形成的概念,而對象則是類具體實現的例子(實例)。以對象為基礎的編程思想具有封裝、繼承和多態三大特徵,在構建量化交易系統中發揮了非常重要的作用。

結尾

最後多說一句,小編是一名python開發工程師,這裡有我自己整理了一套最新的python系統學習教程,包括從基礎的python腳本到web開發、爬蟲、數據分析、數據可視化、機器學習等。

想要這些資料的可以關注小編,並在後臺私信小編:“學習”即可領取。


分享到:


相關文章: