[英]How to solve the problem that the global variable is not defined in python?
我正在為股票/crpyto 交易編寫腳本。 一個很粗略的策略是當RSI(股票的一個技術指標)達到50時買入,當RSI連續3天跌破50或單日跌破40時賣出。 我的原始腳本是這樣的:
def simpleRSIstrategy(indicator, threshold1 = 50, threshold2 = 40, days = 3):
buySellTable = dict()
# simple RSI strategy
hold = False
count = 0
for i in range(len(indicator)):
# buying strategy
if indicator['RSI7'][i] > threshold1 and not hold:
date = indicator.index[i]
buySellTable[date] = 'Buy'
hold = True
# selling strategy
if indicator['RSI7'][i] < threshold1 and hold:
count += 1
if count == days or indicator['RSI7'][i] < threshold2:
date = indicator.index[i]
buySellTable[date] = 'Sell'
hold = False
count = 0
if indicator['RSI7'][i] > threshold1 and hold:
count = 0
return buySellTable
這個腳本應該做的是應用前面提到的簡單 RSI 策略並返回“buySellTable”,這是一個以日期作為鍵、“Buy”和“Sell”作為項目的字典。 這個腳本沒有錯。 由於這是一個粗略的策略,因此有必要對其進行優化。 我想把它分成兩部分——買入策略和賣出策略,這樣我就可以分別進行優化。 然后我將其重寫為以下腳本:
def simpleRSIstrategy_split(indicator, threshold1 = 50, threshold2 = 40, days = 3):
buySellTable = dict()
hold = False
count = 0
startIdx = 0
while True:
simpleBuyStrategy_split(indicator, threshold1)
simpleSellStrategy_split(indicator, threshold1, threshold2, days)
if startIdx == len(indicator)-1:
break
return buySellTable
def simpleBuyStrategy_split(indicator, threshold1):
global startIdx, hold, buySellTable
for i in range(startIdx, len(indicator)):
if indicator['RSI7'][i] > threshold1 and not hold:
date = indicator.index[i]
buySellTable[date] = 'Buy'
hold = True
startIdx = i+1
break
def simpleSellStrategy_split(indicator, threshold1, threshold2, days):
global startIdx, count, hold, buySellTable
for i in range(startIdx, len(indicator)):
if indicator['RSI7'][i] < threshold1 and hold:
count += 1
if count == days or indicator['RSI7'][i] < threshold2:
date = indicator.index[i]
buySellTable[date] = 'Sell'
hold = False
count = 0
startIdx = i+1
break
if indicator['RSI7'][i] > threshold1 and hold:
count = 0
在“simpleRSIstrategy_split”腳本中,我想將 buySellTable、hold、count 和 startIdx 視為可由腳本“simpleBuyStrategy_split”和“simpleSellStrategy_split”使用和編輯的全局變量。 但是我在運行腳本時收到錯誤消息:未定義名稱“startIdx”。 我從控制台檢查 output 並發現當它執行該行時
for i in range(startIdx, len(indicator)):
在腳本“simpleBuyStrategy_split”中,它抱怨未定義 startIdx。 但我已經將其定義為全局變量。 我不知道為什么會這樣,我該如何解決?
顯然你不清楚關鍵字global
的用法。 global
關鍵字用於訪問在 function 內的全局 scope 中定義的變量。考慮這個例子,
var = 0
def func():
var = 2
func()
print (var)
在這里你得到 output:
0
>>>
因為當我們調用func()
時會創建變量var
的本地副本。 var
的這個副本,如果被修改,將不會影響全局副本(即在全局范圍func()
之外定義的var = 0
)。 因此我們得到0
作為 output,因為print (var)
在全局 scope 中。現在考慮這個:
var = 0
def func():
global var
var = 2
func()
print (var)
在這里你得到 output:
2
>>>
因為當func()
時 Python 在全局 scope 中搜索var
,找到它,並將其值更改為2
。 因此print (var)
將顯示2
,因為當我們在global var
之后寫入var = 2
時,全局變量var
在 function 中發生了變化。
但是如果你這樣做:
def func():
global var
var = 2
func()
print (var)
在這里,您在執行print (var)
時得到NameError
,因為var
從未在全局 scope 中定義。 func()
時,Python 在全局 scope 中搜索var
但沒有找到它,因此只是變量的本地副本var
創建。 因此,在這種情況下, global var
變量幾乎是多余的,因為var
從未在全局 scope 中定義。
簡而言之,解決您的問題的方法是
buySellTable = dict()
hold = False
count = 0
startIdx = 0
在 function simpleRSIstrategy_split()
之外。 然后您的代碼應按預期工作。
你弄錯了范圍。 Python 是一種詞法范圍的語言。
不知不覺中,您正在編寫閉包。 這意味着您在simpleBuyStrategy_split
和simpleSellStrategy_split
中使用的變量不在 function 參數列表中 - 但在 function 定義之外。
基本上
buySellTable = dict()
hold = False
count = 0
startIdx = 0
您將它們視為具有動態作用域。 (它們會根據調用這兩個函數的位置而改變)。
由於 python arguments 主要是按引用調用(由於性能原因) - 所以它們像 C 指針一樣工作,如果它們在 function 主體中發生更改,則 arguments 本身也會更改 - 除非它們在 function 主體中被明確地深度復制主體34560845608 .
因此,如果您只是將它們作為 function arguments 傳遞,您就可以像在動態范圍內一樣使用它們。
像這樣:
# first define them globally
buySellTable = dict()
hold = False
count = 0
startIdx = 0
# then in each function definition, pass them as function arguments
def simpleRSIstrategy_split(indicator, threshold1 = 50, threshold2 = 40, days = 3, buySellTable=buySellTable, hold=hold, count=count, startIdx=startIdx):
while True:
simpleBuyStrategy_split(indicator, threshold1, buySellTable=buySellTable, hold=hold, count=count, startIdx=startIdx)
simpleSellStrategy_split(indicator, threshold1, threshold2, days, buySellTable=buySellTable, hold=hold, count=count, startIdx=startIdx)
if startIdx == len(indicator)-1:
break
return buySellTable
def simpleBuyStrategy_split(indicator, threshold1, buySellTable=buySellTable, hold=hold, count=count, startIdx=startIdx):
for i in range(startIdx, len(indicator)):
if indicator['RSI7'][i] > threshold1 and not hold:
date = indicator.index[i]
buySellTable[date] = 'Buy'
hold = True
startIdx = i+1
break
def simpleSellStrategy_split(indicator, threshold1, threshold2, days, buySellTable=buySellTable, hold=hold, count=count, startIdx=startIdx):
for i in range(startIdx, len(indicator)):
if indicator['RSI7'][i] < threshold1 and hold:
count += 1
if count == days or indicator['RSI7'][i] < threshold2:
date = indicator.index[i]
buySellTable[date] = 'Sell'
hold = False
count = 0
startIdx = i+1
break
if indicator['RSI7'][i] > threshold1 and hold:
count = 0
我明白了,不知何故你意識到他們有全局引用,但是,simpleRSI function split 也必須這樣做:
buySellTable = dict()
hold = False
count = 0
startIdx = 0
def simpleRSIstrategy_split(indicator, threshold1 = 50, threshold2 = 40, days = 3):
global buySellTable, hold, count, startIdx
while True:
simpleBuyStrategy_split(indicator, threshold1)
simpleSellStrategy_split(indicator, threshold1, threshold2, days)
if startIdx == len(indicator)-1:
break
return buySellTable
然后其他兩個功能就像您擁有的那樣。 這也可能有效。
但這不是很好。 因為應該避免使用全局變量。
在 Python 中,您將通過使用類來避免使用全局變量。 您可以創建一個 class ,其中全局變量是 class 屬性。 (前置: self.
這三個函數是class的方法。因為它們必須使用self.
你不需要它們中的“全局”聲明。class本身被封裝了。
像這樣:
class SimpleRSITrader:
def __init__(self, indicator, threshold1 = 50, threshold2 = 40, days = 3, rsi='RSI7'):
self.buy_sell_table = {}
self.hold = False
self.count = 0
self.start_idx = 0
self.indicator = indicator
self.thresh1 = threshold1
self.thrseh2 = threshold2
self.days = days
self.rsi = rsi
def run(self):
while True:
self.buy()
self.sell()
if self.startIdx == len(self.indicator)-1:
break
return self.buy_sell_table
def buy(self):
for i in range(self.start_idx, len(self.indicator)):
if self.indicator[self.rsi][i] > self.thresh1 and not self.hold:
date = self.indicator.index[i]
self.buy_sell_table[date] = 'Buy'
self.hold = True
self.start_idx = i + 1
break
def sell(self):
for i in range(self.start_idx, len(self.indicator)):
if self.hold:
if self.indictaor[self.rsi] < self.thresh1:
self.count += 1
if count == self.days or self.indictoar[self.rsi][i] < self.thresh2:
date = self.indicator.index[i]
self.buy_sell_table[date] = 'Sell'
self.hold = False
self.count = 0
self.start_idx = i + 1
break
if self.indicator[self.rsi][i] > self.thresh1:
self.count = 0
這樣做的好處是,您可以隨時從外部檢查屬性的狀態。 因此也與程序的其他部分進行通信 - 雖然你沒有全局變量 - 因為以前的全局變量現在封裝在 class 中 - 因此打包到對象中。
因此,我們的想法是,對於每個具有新閾值設置和日期設置的指標,您都可以為簡單 RSI 策略創建一個新實例。
這個 class,您甚至可以重復使用不同的 RSI(“RSI7”除外)。
通過封裝,您買賣的上下文變得清晰 - 因此方法名稱或屬性名稱也變得更簡單 - 因此一切都更易於閱讀並且您的代碼結構化 - 每個以前的全局變量和函數現在都封裝到 class - 因此代碼結構清晰,每個代碼的上下文都清晰 - 因此您可以使用更短的名稱。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.