3 个不稳定版本
使用旧的 Rust 2015
0.2.1 | 2016年6月17日 |
---|---|
0.2.0 |
|
0.1.1 | 2016年4月18日 |
0.1.0 | 2016年4月14日 |
在金融类别中排名第215位
40KB
433 行
StockFighter
StockFighter 的完整 Rust API
在此处找到它:https://crates.io/crates/stockfighter
使用最新的 nightly:multirust run nightly cargo build
请确保设置 $STOCKFIGHTER_API_KEY
.
简介
易于开始
extern crate stockfighter;
use stockfighter::*;
fn main() {
let sf = StockFighter::new();
println!("{:#?}",sf.venue_heartbeat(Venue("TESTEX".to_string())));
}
完全异步和非阻塞示例,使用 WS 和 carboxyl FRP 库!
#[macro_use] extern crate stockfighter;
extern crate serde_json;
extern crate ws;
extern crate carboxyl;
use std::io::prelude::*;
use std::fs::{File,OpenOptions};
use std::thread;
use std::sync::mpsc::channel;
use std::sync::mpsc::Sender as MPSCSender;
use ws::{connect,CloseCode,Handler,Message,Handshake,Result,Error,ErrorKind};
use carboxyl::{Sink,Stream};
use stockfighter::*;
enum SFTTEvent { Connect(Stream<QuoteM>), Disconnect }
struct SFTT { tt_sender: MPSCSender<SFTTEvent>, tt_sink: Sink<QuoteM>, f: File }
impl Handler for SFTT {
fn on_open(&mut self, _: Handshake) -> Result<()> {
println!("{}","SFTT connected!");
self.tt_sender.send(SFTTEvent::Connect(self.tt_sink.stream())).map_err(|err| Error::new(
ErrorKind::Internal, format!("Unable to communicate between threads: {:?}.", err))) }
fn on_message(&mut self, msg: Message) -> Result<()> {
if let Err(e) = self.f.write(&msg.clone().into_data()) { println!("{}", e); }
match serde_json::from_str::<QuoteWS>(&msg.into_text().unwrap()) {
Ok(o) => { Ok(self.tt_sink.send_async(quote_ws_to_quote_m(o))) },
Err(e) => { Err(Error::new(ErrorKind::Internal, format!("SFTT on_message: {:?}.", e))) }}}
fn on_close(&mut self, code: CloseCode, reason: &str) { }
fn on_error(&mut self, err: Error) { }}
fn main() {
let sf = StockFighter::new();
let acc = "";
let venue = "";
let stock = "";
let tt_url = format!("wss://api.stockfighter.io/ob/api/ws/{}/venues/{}/tickertape/stocks/{}",acc,venue,stock);
let (tt_tx, tt_rx) = channel();
let _ = thread::Builder::new().name("sf_tt".to_owned()).spawn(move || {
connect(tt_url, |_| { SFTT { tt_sender: tt_tx.clone(),
tt_sink: Sink::new(),
f: OpenOptions::new().write(true).append(true).open("data/orders.txt").unwrap()
}}).unwrap(); });
let tt_stream = match tt_rx.recv() { Ok(SFTTEvent::Connect(stream)) => stream, _ => panic!("tt_sink") };
let _ = thread::Builder::new().name("trade".to_owned()).spawn(move || {
for event in tt_stream.events() {
if let Ok(SFTTEvent::Disconnect) = tt_rx.try_recv() { break }
/* trade! */ }}).unwrap(); }
函数、类型和接口
// Internal Structs and Funcs
pub struct HyperResult<T>(pub Result<Result<T,hyper::status::StatusCode>,hyper::error::Error>);
impl<T> HyperResult<T> {
pub fn all_ok(self) -> T {..} }
pub struct StockFighter { api_key: String, client: Client }
impl StockFighter {
pub fn new() -> StockFighter {..}
pub fn api_heartbeat(&self) -> HyperResult<ApiHeartbeat> {..}
pub fn venue_heartbeat(&self, venue: Venue) -> HyperResult<VenueHeartbeat> {..}
pub fn stocks_on_venue(&self, venue: Venue) -> HyperResult<StocksOnVenue> {..}
pub fn orderbook(&self, venue: Venue, stock: Symbol) -> HyperResult<OrderbookForAStock> {..}
pub fn new_order(&self, acc: Account, venue: Venue, stock: Symbol,
price: Price, qty: Qty, dir: Direction, order_type: OrderType)
-> HyperResult<NewOrderForAStock> {..}
pub fn quote(&self, venue: Venue, stock: Symbol) -> HyperResult<QuoteForAStock> {..}
pub fn status_for_existing_order(&self, id: OrderId, venue: Venue, stock: Symbol)
-> HyperResult<StatusForAnExistingOrder> {..}
pub fn cancel_order(&self, venue: Venue, stock: Symbol, order: OrderId)
-> HyperResult<CancelAnOrder> {..}
pub fn status_for_all_orders(&self, venue: Venue, acc: Account)
-> HyperResult<StatusForAllOrders> {..}
pub fn status_for_all_orders_in_a_stock(&self, venue: Venue, acc: Account, stock: Symbol)
-> HyperResult<StatusForAllOrdersInAStock> {..} }
pub fn quote_to_quote_m(q: Quote) -> QuoteM {..}
pub fn quote_ws_to_quote_m(q: QuoteWS) -> QuoteM {..}
// Enums
pub enum VenueHeartbeat { R200(VenueOk), R500(ErrMsg), R404(ErrMsg) }
pub enum StocksOnVenue { R200(Stocks), R404(ErrMsg) }
pub enum OrderbookForAStock { R200(Orderbook), R404(ErrMsg) }
pub enum NewOrderForAStock { R200(Order), R404(ErrMsg), R200Err(ErrMsg) }
pub enum QuoteForAStock { R200(QuoteM), R404(ErrMsg) }
pub enum StatusForAnExistingOrder { R200(Order), R401(ErrMsg) }
pub enum CancelAnOrder { R200(Order), R401(ErrMsg) }
pub enum StatusForAllOrders { R200(Status), R401(ErrMsg) }
pub enum StatusForAllOrdersInAStock { R200(Status), R401(ErrMsg) }
pub enum Direction { Buy, Sell }
pub enum OrderType { Limit, Market, FillOrKill, ImmediateOrCancel }
// API Structs
pub struct ApiHeartbeat(pub ErrMsg);
pub struct ErrMsg { pub ok: bool, pub error: String }
pub struct VenueOk { ok: bool, venue: Venue }
pub struct Qty(pub usize);
pub struct Price(pub usize);
pub struct Venue(pub String);
pub struct Symbol(pub String);
pub struct Account(pub String);
pub struct DTUTC(pub DateTime<UTC>);
pub struct Stocks { pub ok: bool, pub symbols: Vec<SymbolName> }
pub struct SymbolName { pub name: String, pub symbol: Symbol }
pub struct Orderbook {
pub ok: bool,
pub venue: Venue,
pub symbol: Symbol,
pub bids: Bids,
pub asks: Asks,
pub ts: DTUTC }
pub struct Bids(pub Vec<Position>);
pub struct Asks(pub Vec<Position>);
pub struct Position {
pub price: Price,
pub qty: Qty,
pub is_buy: IsBuy }
pub struct IsBuy(pub bool);
pub struct Order {
pub ok: bool,
pub symbol: Symbol,
pub venue: Venue,
pub direction: Direction,
pub original_qty: OriginalQty,
pub qty: Qty,
pub price: Price,
pub order_type: OrderType,
pub id: OrderId,
pub account: Account,
pub ts: DTUTC,
pub fills: Vec<Fill>,
pub total_filled: TotalFilled,
pub open: OrderOpen }
pub struct OriginalQty(pub usize);
pub struct OrderId(pub usize);
pub struct TotalFilled(pub usize);
pub struct OrderOpen(pub bool);
pub struct Fill { pub price: Price, pub qty: Qty, pub ts: DTUTC }
pub struct NewOrder {
pub account: Account,
pub venue: Venue,
pub stock: Symbol,
pub qty: Qty,
pub price: Price,
pub direction: Direction,
pub order_type: OrderType }
pub struct Quote {
pub ok: Option<bool>,
pub symbol: Symbol,
pub venue: Venue,
pub bid: Option<Bid>,
pub ask: Option<Ask>,
pub bid_size: Option<BidSize>,
pub ask_size: Option<AskSize>,
pub bid_depth: Option<BidDepth>,
pub ask_depth: Option<AskDepth>,
pub last_size: Option<LastSize>,
pub last_trade: Option<DTUTC>,
pub quote_time: Option<DTUTC> }
pub struct QuoteM {
pub symbol: Symbol,
pub venue: Venue,
pub these_quotes: TheseQuotes,
pub last: Last,
pub last_size: LastSize,
pub last_trade: DTUTC,
pub quote_time: DTUTC }
pub enum TheseQuotes { ThisBid(BidStruct), ThatAsk(AskStruct), TheseQuotes(BidStruct,AskStruct), Empty }
pub struct BidStruct { bid: Bid, bid_size: BidSize, bid_depth: BidDepth }
pub struct AskStruct { ask: Ask, ask_size: AskSize, ask_depth: AskDepth }
pub struct Bid(pub usize);
pub struct Ask(pub usize);
pub struct BidSize(pub usize);
pub struct AskSize(pub usize);
pub struct BidDepth(pub usize);
pub struct AskDepth(pub usize);
pub struct Last(pub usize);
pub struct LastSize(pub usize);
pub struct Status {
pub ok: bool,
pub venue: Venue,
pub orders: Vec<Order> }
pub struct QuoteWS { pub ok: bool, pub quote: Quote }
pub struct FillsWS {
pub ok: bool,
pub account: Account,
pub venue: Venue,
pub symbol: Symbol,
pub order: Order,
pub standing_id: StandingId,
pub incoming_id: IncomingId,
pub price: Price,
pub filled: Filled,
pub filled_at: DTUTC,
pub standing_complete: StandingComplete,
pub incoming_complete: IncomingComplete }
pub struct StandingId(pub usize);
pub struct IncomingId(pub usize);
pub struct Filled(pub usize);
pub struct StandingComplete(pub bool);
pub struct IncomingComplete(pub bool);
新类型
所有具有类似底层表示但语义上不同的类型都被封装在新类型中。
新类型在编译时被擦除,仅用于防止库用户意外传递错误的参数,例如将 OrderId 传递给 Price 参数。
所有新类型也实现了 Deref
、DerefMut
、From
和 Into
。
新类型 2
它们还可以防止在非相似类型之间进行操作,例如将价格和数量相乘。
如果您确定您想要这样做,您可以使用元组语法简单地访问底层数据。
例如
let price_times_qty = price.0 * qty.0;
新类型 3
所有新类型也实现了在适用的情况下重载操作符,因此您不需要解包和重新包装新类型来对其底层值进行操作。
例如
let multiplied_prices = Price(3241) * Price(1748);
响应类型
所有请求都返回一个 HyperRequest(如果一切顺利),该请求由两个 Ok 构成,该类型定义如下
pub type HyperResult<T> = Result<Result<T,hyper::status::StatusCode>,hyper::error::Error>;
使用这种 Ok(Ok(T))
类型的原因是第一层是用于请求本身,如果请求本身出现问题并且返回一个 Err(hyper::error::Error)
,则。
如果请求顺利进行,但API返回的状态不是预期的,那么它将返回意外的状态代码:Ok(Err(hyper::status::StatusCode))
大多数情况下,这两者都会顺利进行,并将返回Ok(Ok(RX0X(T)))
,其中RX0X
是某些状态代码,通常为成功时的R200
,或者在某些错误情况下的R401
、R404
或R500
。如果你的程序期望每次都能正常工作,你可以简单地使用.all_ok()
来展开两个Ok
。
响应类型2
返回的主要响应类型有VenueOk
、ErrMsg
、Stocks
、Orderbook
、Order
、Quote
和Status.
WebSocket响应类型为QuoteWS
和FillsWS
。
下面是一个完整的响应示例
Ok(
Ok(
R200(
Order {
ok: true,
symbol: Symbol(
"CWI"
),
venue: Venue(
"CSJBEX"
),
direction: Sell,
original_qty: OriginalQty(
10
),
qty: Qty(
0
),
price: Price(
6000
),
order_type: Limit,
id: OrderId(
1834
),
account: Account(
"BFB42332882"
),
ts: DTUTC(
"2016-04-14T14:18:28.579888777Z"
),
fills: [
Fill {
price: Price(
6120
),
qty: Qty(
10
),
ts: DTUTC(
"2016-04-14T14:18:28.579891467Z"
)
}
],
total_filled: TotalFilled(
10
),
open: OrderOpen(
false
)
}
)
)
)
响应类型3
原始报价结构体(无论是Quote
还是QuoteWS
)进一步解析为QuoteM
。这个结构体使用了一种类似These
的结构。
如果你不熟悉These
,原始的Haskell库及其定义在此:https://hackage.haskell.org/package/these-0.6.2.1/docs/Data-These.html
为了简单起见,我在下面复制了QuoteM
的定义
pub struct QuoteM {
pub symbol: Symbol,
pub venue: Venue,
pub these_quotes: TheseQuotes,
pub last: Last,
pub last_size: LastSize,
pub last_trade: DTUTC,
pub quote_time: DTUTC }
pub enum TheseQuotes { ThisBid(BidStruct), ThatAsk(AskStruct), TheseQuotes(BidStruct,AskStruct), Empty }
pub struct BidStruct { bid: Bid, bid_size: BidSize, bid_depth: BidDepth }
pub struct AskStruct { ask: Ask, ask_size: AskSize, ask_depth: AskDepth }
你可能已经注意到,TheseQuotes
并不完全像These
,事实上,有一个Empty
情况,用于处理当Orderbook
为空时的情况。换句话说,TheseQuotes
模拟了Orderbook
可能存在的四种状态,即Empty
,有某些Bids
,有某些Asks
,或者同时有Bids
和Asks
。
依赖关系
~5–6.5MB
~117K SLoC