如何解决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"one_answers"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

在"simpleRSStrategy_split"脚本中,我想将buySellTable、hold、count和startIdx视为全局变量,可由脚本"simpleBuyStrategy_split"one_answers"simpleSellStrategy_ssplit"使用和编辑。但我在运行脚本时收到了错误消息:名称"startIdx"未定义。我检查了控制台的输出,发现当它执行线路时

for i in range(startIdx, len(indicator)):

在脚本"simpleBuyStrategy_split"中,它抱怨没有定义startIdx。但我已经把它定义为一个全局变量。我不知道为什么会发生这种情况,我该如何解决?

您的作用域错误。Python是一种词汇范围的语言。

在不知情的情况下,您正在编写闭包。这意味着您在simpleBuyStrategy_splitsimpleSellStrategy_split中使用的变量不在函数参数列表中,但在函数定义之外。

基本上

buySellTable = dict()
hold = False
count = 0 
startIdx = 0

您将它们视为具有动态范围。(它们将根据调用这两个函数的位置而变化)。

由于python参数大多是通过引用调用的(由于性能原因),因此它们的工作方式类似于C指针,因此如果参数在函数体中发生更改,则参数本身也会发生更改,除非它们在函数体内被显式地深度复制。

因此,如果您只是将它们作为函数参数传递,就可以将它们当作动态作用域来使用。

像这样:

# 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

我知道,不知怎么的,你意识到他们必须在全球范围内参考,但是,简单的RSI函数分割也必须做到这一点:


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中,您可以通过使用类来避免全局变量。您可以创建一个全局变量为类Attributes的类。(编写人:self.这三个函数是类的方法。由于他们必须使用self.引用这四个变量你不需要他们身上的"全球"三角洲。并且类本身被封装。

像这样:

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

这样做的好处是,您可以随时从外部检查属性的状态。因此,还可以与程序的其他部分进行通信,而您没有全局变量,因为以前的全局变量现在被封装在类中,因此被打包到对象中。

因此,我们的想法是,对于每个具有新阈值设置和日期设置的指标,您可以为简单的RSI策略创建一个新的实例。

这个类,您甚至可以重用不同的RSI(除了"RSI7")。

通过封装,买卖的上下文变得清晰-因此方法名称或属性名称也变得更简单-因此一切都更容易阅读,代码也更结构化-以前的每个全局变量和函数现在都封装到类中-因此代码的结构清晰,每个全局变量的上下文都清晰-因此你的名称可以更短。

显然您不清楚关键字global的用法。global关键字用于访问函数内部全局范围中定义的变量。考虑这个例子,

var = 0
def func():
var = 2
func()
print (var)

在这里你可以得到输出:

0
>>>

因为当我们调用CCD_ 8时会创建变量CCD_。如果修改了var的此副本,则不会影响全局副本(即全局范围中在func()之外定义的var = 0)。因此,我们得到0作为输出,因为print (var)在全局范围内。现在考虑一下:

var = 0
def func():
global var
var = 2
func()
print (var)

在这里你可以得到输出:

2
>>>

因为当调用func()时,Python在全局范围内搜索var,找到它,并将其值更改为2。因此,print (var)将显示2,因为当我们在global var之后写入var = 2时,函数内部的全局变量var发生了更改。

但如果你这样做:

def func():
global var
var = 2
func()
print (var)

在这里,您在执行print (var)时得到NameError,因为var从未在全局范围中定义过。当调用func()时,Python在全局范围内搜索var,但没有找到它,因此只创建变量var的本地副本。所以global var在这种情况下是非常多余的,因为var从未在全局范围中定义过。

简单地说,你的问题的解决方案是放置

buySellTable = dict()
hold = False
count = 0 
startIdx = 0

在函数CCD_ 30之外。然后,您的代码应该可以按预期工作。