这里我有一个程序来轮询队列中的事件,如果发现它执行了一个REST API的命令。此外,如果找到某个事件,它会打印我需要用作stopLoss
的当前价格。这段代码完全按照我的意愿运行,但是,当我尝试在rates()
内调用函数__main__
时,程序就会停止运行。
删除引用stopLoss = rates()
,程序运行良好而没有stopLoss
,但我需要-.001
作为我的stopLoss
。
代码如下:
import Queue
import threading
import time
import json
import oandapy
from execution import Execution
from settings import STREAM_DOMAIN, API_DOMAIN, ACCESS_TOKEN, ACCOUNT_ID
from strategy import TestRandomStrategy
from streaming import StreamingForexPrices
#polls API for Current Price
def stop():
while True:
oanda = oandapy.API(environment="practice", access_token="xxxxxx")
response = oanda.get_prices(instruments="EUR_USD")
prices = response.get("prices")
asking_price = prices[0].get("ask")
s = asking_price - .001
return s
#Checks for events and executes order
def trade(events, strategy, execution):
while True:
try:
event = events.get(False)
except Queue.Empty:
pass
else:
if event is not None:
if event.type == 'TICK':
strategy.calculate_signals(event)
elif event.type == 'ORDER':
print
execution.execute_order(event)
def rates(events):
while True:
try:
event = events.get(False)
except Queue.Empty:
pass
else:
if event.type == 'TICK':
r = stop()
print r
if __name__ == "__main__":
heartbeat = 0 # Half a second between polling
events = Queue.Queue()
# Trade 1 unit of EUR/USD
instrument = "EUR_USD"
units = 1
stopLoss = rates() #Problem area!!!!!!>>>>>>>>>>>>>>//////////////////////////
prices = StreamingForexPrices(
STREAM_DOMAIN, ACCESS_TOKEN, ACCOUNT_ID,
instrument, events
)
execution = Execution(API_DOMAIN, ACCESS_TOKEN, ACCOUNT_ID)
strategy = TestRandomStrategy(instrument, units, events, stopLoss)
#Threads
trade_thread = threading.Thread(target=trade, args=(events, strategy, execution))
price_thread = threading.Thread(target=prices.stream_to_queue, args=[])
stop_thread = threading.Thread(target=rates, args=(events,))
# Start both threads
trade_thread.start()
price_thread.start()
stop_thread.start()
答案 0 :(得分:1)
到目前为止还没有答案,所以我会试试。
您的主要问题似乎是,您不知道如何在线程之间交换数据
首先是价格问题。
这里的循环:
while True:
oanda = oandapy.API(environment="practice", access_token="xxxxxx")
response = oanda.get_prices(instruments="EUR_USD")
prices = response.get("prices")
asking_price = prices[0].get("ask")
s = asking_price - .001
return s
无效,因为return s
会自动突破它。
所以你需要的是一个存储s
的共享变量。您可以使用threading.Lock
保护对其的访问权限。最简单的方法是将Thread子类化,并使s
像这样的实例属性(我将其命名为price
):
class PricePoller(threading.Thread):
def __init__(self, interval):
super(PricePoller, self).__init__()
# private attribute, will be accessed as property via
# threadsafe getter and setter
self._price = None
# lock guarding access to _price
self._dataLock = threading.Lock()
# polling interval
self.interval = interval
# set this thread as deamon, so it will be killed when
# the main thread dies
self.deamon = True
# create an event that allows us to exit the mainloop
# and terminate the thread safely
self._stopEvent = threading.Event()
def getPrice(self):
# use _dataLock to get threadsafe access to self._price
with self._dataLock:
return self._price
def setPrice(self, price)
# use _dataLock to get threadsafe access to self._price
with self._dataLock:
self._price = price
price = property(getPrice, setPrice, None)
def run(self):
while not self.stopEvent.isSet():
oanda = oandapy.API(environment="practice", access_token="xxxxxx")
response = oanda.get_prices(instruments="EUR_USD")
prices = response.get("prices")
asking_price = prices[0].get("ask")
self.price = asking_price - .001
time.sleep(self.interval) # don't spam the server
def stop(self):
self._stopEvent.set()
然后可以通过以下方式开始:
poller = PricePoller(heartbeat)
poller.start()
您可以随时随地poller.price
获取价格!如果愿意,您甚至可以将轮询器传递给其他线程。
但!如果您试图在poller.start()
之后立即获得价格,那么您肯定会获得None
。为什么这个? poller.start()
没有阻止,因此当你的主线程正在进行并试图获得第一个价格时,你的轮询器甚至没有完成启动!
怎么解决这个?引入另一个threading.Event
并使用其函数wait
让主线程等待,直到轮询器线程设置它为止。我将实施留给您。
我只是猜测这就是你想要的......只看你的代码,你不必将stop
函数放在一个根本的线程中,而你可以将stopLess = rates()
替换为stopLess = stop()
,因为您不会在任何地方更新价格轮询的结果!但我认为你想在某些时候这样做,否则将它放入一个线程是没有意义的。
现在到队列和你的'事件流' 这个片段:
try:
event = events.get(False)
except Queue.Empty:
pass
也可以是:
event = events.get()
无论如何你不做任何事情,最好让Queue
处理等待事件。
然后,据我所知,你有两个线程调用Queue.get
,但是这个函数会在检索后删除队列中的元素!这意味着无论谁先获得事件,消耗它,而另一个线程永远不会看到它。但是对于轮询器的上述解决方案,我认为你可以摆脱stop_thread
,这也解决了这个问题。
现在一般关于线程的说明
一个线程有自己的“调用链”,它在run
方法(或者你提供target
的方法中)开始,如果你不是子类的话。
这意味着run
调用的任何函数都由此线程执行,并且所有函数依次由此调用(依此类推)。但是,两个线程完全可能同时执行相同的功能!如果你不使用同步手段(例如事件,锁或障碍),则无法知道哪个线程在某个时间执行代码的哪个部分。
如果在被调用函数中使用的所有变量都是本地矿石在调用函数中是本地的,那么这不是问题:
def onlyLocal(x, n):
if n == 0:
return x
return onlyLocal(x*2, n-1)
或专门阅读:
def onlyRead(myarray):
t = time.time()
return t - sum(myarray)
但是,只要您同时从多个线程读取和写入变量,就需要保护对这些变量的访问权限,否则如果您传递多个线程已知的对象(例如self
) ):
def setPrice(self, price):
self._price = price
或者如果你的函数使用来自多个线程所占据的外部作用域的变量:
def variableFromOutside(y):
global x
x += y
return y
你可以永远不会确定没有一个线程(2)更改你刚刚读过的变量,当你正在处理它时,然后用更无效的值更新它。
global x ; Thread1 ; Thread2 ;
2 ; y = x ; z = x ;
2 ; y **= 3 ; x = z+1 ;
3 ; x = y-4 ; return ;
4 ; return ; ... ;
这就是为什么你必须使用锁来保护对这些变量的访问。使用锁定(l
):
global x ; Thread1 ; Thread2 ;
2 ; l.acqcuire() ; l.acquire() ;
2 ; y = x ; | ;
2 ; y **= 3 ; | ;
2 ; x = y-4 ; | ;
4 ; l.release() ; v ;
4 ; return ; z = x ;
4 ; ... ; x = z+1 ;
5 ; ... ; l.release() ;
这里Thread1在Thread2之前获取锁。因此,Thread2必须等到Thread1再次释放锁定,然后再调用acquire
使用acquire
时会自动调用release
和with lock:
。
还要注意,在这个玩具示例中,可能是Thread2在Thread1之前获得锁定的情况,但至少它们仍然不会相互干扰。
这是一个关于大型主题的简要介绍,阅读了一些关于线程并行化的内容并使用它。没有比练习更好的学习方法 我已在浏览器中编写此代码,因此未已经过测试!如果有人发现问题,请在评论中告诉我,或者随时直接更改。