sparkstartconsulting / ibkr-api-rust Goto Github PK
View Code? Open in Web Editor NEWPort of Interactive Broker's trading API written in Rust
License: MIT License
Port of Interactive Broker's trading API written in Rust
License: MIT License
See here.
At the moment examples
and tests all live
under src
. This also means there is a namespace prefix, core
, that could potentially be omitted.
If they were moved to their own resp. folders this crate could be lighter. It may be necessary to move examples
to a twsapi-examples
crate. The tests could then depend on this crate.
See the bevy
crate for some ideas how to set up a workspace with sub-crates that makes this more easy.
P.S.: I may do this myself for my fork and open a PR here.
I'm trying to use req_historical_data()
with a future.
In test_helpers.rs
, I replaced next_valid_id()
with the code to generate the request to make this simpler to replicate:
fn next_valid_id(&mut self, order_id: i32) {
self.next_order_id = order_id;
info!("next_valid_id -- order_id: {}", order_id);
let order_id = self.next_order_id();
let time_str = Utc::now().format("%Y%m%d %H:%M:%S");
self.client
.as_ref()
.expect(CLIENT_IS_NONE)
.lock()
.expect(CLIENT_POISONED_MUTEX)
.req_historical_data(
order_id,
&Contract {
symbol: "MNQ".to_string(),
sec_type: "FUT".to_string(),
exchange: "GLOBEX".to_string(),
last_trade_date_or_contract_month: "20211217".to_string(),
..Default::default()
},
time_str.to_string().as_str(),
"3 D",
"30 secs",
"TRADES",
0,
1,
false,
vec![],
)
.unwrap();
}
The output I get is:
2021-10-11 19:14:10 twsapi_client src/bin/manual_tests.rs line: 32 INFO - getting connection...
2021-10-11 19:14:10 twsapi::core::client src/core/client.rs line: 118 INFO - Connecting
2021-10-11 19:14:15 twsapi::core::client src/core/client.rs line: 171 INFO - Server version: 142
2021-10-11 19:14:15 twsapi::core::client src/core/client.rs line: 186 INFO - Connected
2021-10-11 19:14:15 twsapi::core::reader src/core/reader.rs line: 109 DEBUG - starting reader loop
2021-10-11 19:14:15 twsapi::core::decoder src/core/decoder.rs line: 2598 INFO - Starting run...
2021-10-11 19:14:15 twsapi::core::decoder src/core/decoder.rs line: 1278 INFO - calling managed_accounts
2021-10-11 19:14:15 twsapi::examples::test_helpers src/examples/test_helpers.rs line: 3341 INFO - managed_accounts -- accounts_list: DF3987931,DU3987932,DU3987933,DU3987934,DU3987935,DU3987936,
2021-10-11 19:14:15 twsapi::core::decoder src/core/decoder.rs line: 1283 INFO - finished calling managed_accounts
2021-10-11 19:14:15 twsapi::examples::test_helpers src/examples/test_helpers.rs line: 3222 INFO - next_valid_id -- order_id: 1
2021-10-11 19:14:15 twsapi::examples::test_helpers src/examples/test_helpers.rs line: 3031 ERROR - req_id: -1 ,error_code: 2104 , error_string:Market data farm connection is OK:usfarm
2021-10-11 19:14:15 twsapi::examples::test_helpers src/examples/test_helpers.rs line: 3031 ERROR - req_id: -1 ,error_code: 2106 , error_string:HMDS data farm connection is OK:ushmds
2021-10-11 19:14:15 twsapi::core::reader src/core/reader.rs line: 101 DEBUG - more incoming packet(s) are needed
2021-10-11 19:14:15 twsapi::core::reader src/core/reader.rs line: 101 DEBUG - more incoming packet(s) are needed
2021-10-11 19:14:15 twsapi::core::reader src/core/reader.rs line: 101 DEBUG - more incoming packet(s) are needed
The last message, more incoming packet(s) are needed
, repeats infinitely.
Some test contract data (pulled from IB's test contracts) are not valid
I'm working through your code now, thanks for making this project, so sorry if I just haven't found the right way to construct a client yet that can do this.
let wrapper: Arc<Mutex<TestWrapper<TcpStreamer>>> = Arc::new(Mutex::new(TestWrapper::new()));
let app = Arc::new(Mutex::new(EClient::new(wrapper.clone())));
println!("getting connection...");
wrapper.lock().expect("Wrapper mutex was poisoned").client = Option::from(app.clone());
app
.lock()
.expect("EClient mutex was poisoned")
.connect("192.168.0.118", 7555, 0)?;
That connect call throws a bunch of errors if the API is in read-only mode:
["4", "2", "-1", "2104", "Market data farm connection is OK:usfarm.nj"]["4", "2", "-1", "2104", "Market data farm connection is OK:cashfarm"]["4", "2", "-1", "2104", "Market data farm connection is OK:usfarm"]["4", "2", "-1", "2106", "HMDS data farm connection is OK:euhmds"]["4", "2", "-1", "2106", "HMDS data farm connection is OK:cashhmds"]["4", "2", "-1", "2106", "HMDS data farm connection is OK:fundfarm"]["4", "2", "-1", "2106", "HMDS data farm connection is OK:ushmds"]["4", "2", "-1", "2158", "Sec-def data farm connection is OK:secdefnj"]["4", "2", "2147483647", "321", "Error validating request.-\'cB\' : cause - The API interface is currently in Read-Only mode."]...
It is unexpected that the initial connect call would require write access. Happy to work on this if desired.
Issue seems to be in either the order or number of fields when constructing the message to place an order for combos
I changed the example contract_samples::simple_future()
for TSLA
in historical_data_operations_req() like so:
pub fn historical_data_operations_req(&self) -> Result<(), IBKRApiLibError> {
// Requesting historical data
let contract = Contract {
symbol: "TSLA".to_string(),
sec_type: "STK".to_string(),
exchange: "SMART".to_string(),
currency: "USD".to_string(),
..Default::default()
};
self.client
.as_ref()
.unwrap()
.try_lock()
.unwrap()
.req_head_time_stamp(
4101,
&contract,
"TRADES",
0,
1,
)?;
let dt = Utc::now();
let query_time = dt.format("%Y%m%d %H:%M:%S").to_string();
info!("Request Time: {}", query_time);
self.client
.as_ref()
.expect(CLIENT_IS_NONE)
.lock()
.expect(CLIENT_POISONED_MUTEX)
.req_historical_data(
4102,
&contract,
query_time.as_str(),
"60 D",
"1 day",
"MIDPOINT",
1,
1,
false,
vec![],
)?;
self.client
.as_ref()
.expect(CLIENT_IS_NONE)
.lock()
.expect(CLIENT_POISONED_MUTEX)
.req_historical_data(
4103,
&contract,
query_time.as_str(),
"10 D",
"1 min",
"TRADES",
1,
1,
false,
vec![],
)?;
self.client
.as_ref()
.expect(CLIENT_IS_NONE)
.lock()
.expect(CLIENT_POISONED_MUTEX)
.req_historical_data(
4104,
&contract,
"",
"1 M",
"1 day",
"MIDPOINT",
1,
1,
true,
vec![],
)?;
Ok(())
}
What I get is No market data permissions for ISLAND STK
for each of the three req_historical_data()
calls. Only the req_head_time_stamp()
calls succeeds:
2021-10-04 18:06:29 twsapi::examples::test_helpers src/examples/test_helpers.rs line: 3403 INFO - fundamental_data -- req_id: 4101, delta_neutral_contract: 20100629 08:00:00
2021-10-04 18:06:29 twsapi::examples::test_helpers src/examples/test_helpers.rs line: 3038 ERROR - req_id: 4102 ,error_code: 162 , error_string:Historical Market Data Service error message:No market data permissions for ISLAND STK
2021-10-04 18:06:29 twsapi::examples::test_helpers src/examples/test_helpers.rs line: 3038 ERROR - req_id: 4103 ,error_code: 162 , error_string:Historical Market Data Service error message:No market data permissions for ISLAND STK
2021-10-04 18:06:29 twsapi::examples::test_helpers src/examples/test_helpers.rs line: 3038 ERROR - req_id: 4104 ,error_code: 162 , error_string:Historical Market Data Service error message:No market data permissions for ISLAND STK
I tried the same from the Python ib sync
API and it works. What am I missing here?
Edit: So I saw this is already published. I suggest changing the repo name to twsapi
. I work on docs (make sure all links work) and update Cargo.toml
& README.md
and open a PR against this issue.
Maybe just ibkr
or ib-kr
?
I am trying to get a similar data structure like the Ticker
class in the Python ib_sync lib filled.
I am looking for something like the output from this Python code snippet:
0 async tick Stock: 788.68 Ticker(contract=Stock(symbol='TSLA', exchange='SMART', currency='USD'), time=datetime.datetime(2021, 9, 29, 15, 3, 8, 301215, tzinfo=datetime.timezone.utc), bid=788.6, bidSize=1, ask=788.83, askSize=3, last=788.68, lastSize=1, volume=68131, open=779.8, high=793.5, low=779.1, close=777.56, halted=0.0, ticks=[TickData(time=datetime.datetime(2021, 9, 29, 15, 3, 8, 301215, tzinfo=datetime.timezone.utc), tickType=1, price=788.6, size=1), TickData(time=datetime.datetime(2021, 9, 29, 15, 3, 8, 301215, tzinfo=datetime.timezone.utc), tickType=2, price=788.83, size=3), TickData(time=datetime.datetime(2021, 9, 29, 15, 3, 8, 301215, tzinfo=datetime.timezone.utc), tickType=4, price=788.68, size=1), TickData(time=datetime.datetime(2021, 9, 29, 15, 3, 8, 301215, tzinfo=datetime.timezone.utc), tickType=5, price=788.68, size=1), TickData(time=datetime.datetime(2021, 9, 29, 15, 3, 8, 301215, tzinfo=datetime.timezone.utc), tickType=8, price=-1.0, size=68131), TickData(time=datetime.datetime(2021, 9, 29, 15, 3, 8, 301215, tzinfo=datetime.timezone.utc), tickType=6, price=793.5, size=0), TickData(time=datetime.datetime(2021, 9, 29, 15, 3, 8, 301215, tzinfo=datetime.timezone.utc), tickType=7, price=779.1, size=0), TickData(time=datetime.datetime(2021, 9, 29, 15, 3, 8, 301215, tzinfo=datetime.timezone.utc), tickType=9, price=777.56, size=0), TickData(time=datetime.datetime(2021, 9, 29, 15, 3, 8, 301215, tzinfo=datetime.timezone.utc), tickType=14, price=779.8, size=0), TickData(time=datetime.datetime(2021, 9, 29, 15, 3, 8, 301215, tzinfo=datetime.timezone.utc), tickType=49, price=0.0, size=0)])
0 async tick Stock: 329.33 Ticker(contract=Stock(symbol='UPST', exchange='SMART', currency='USD'), time=datetime.datetime(2021, 9, 29, 15, 3, 8, 323455, tzinfo=datetime.timezone.utc), bid=329.08, bidSize=1, ask=329.5, askSize=1, last=329.33, lastSize=1, volume=22554, open=319.01, high=332.1, low=317.33, close=313.4, halted=0.0, ticks=[TickData(time=datetime.datetime(2021, 9, 29, 15, 3, 8, 323455, tzinfo=datetime.timezone.utc), tickType=1, price=329.08, size=1), TickData(time=datetime.datetime(2021, 9, 29, 15, 3, 8, 323455, tzinfo=datetime.timezone.utc), tickType=2, price=329.5, size=1), TickData(time=datetime.datetime(2021, 9, 29, 15, 3, 8, 323455, tzinfo=datetime.timezone.utc), tickType=4, price=329.33, size=1), TickData(time=datetime.datetime(2021, 9, 29, 15, 3, 8, 323455, tzinfo=datetime.timezone.utc), tickType=5, price=329.33, size=1), TickData(time=datetime.datetime(2021, 9, 29, 15, 3, 8, 323455, tzinfo=datetime.timezone.utc), tickType=8, price=-1.0, size=22554), TickData(time=datetime.datetime(2021, 9, 29, 15, 3, 8, 323455, tzinfo=datetime.timezone.utc), tickType=6, price=332.1, size=0), TickData(time=datetime.datetime(2021, 9, 29, 15, 3, 8, 323455, tzinfo=datetime.timezone.utc), tickType=7, price=317.33, size=0), TickData(time=datetime.datetime(2021, 9, 29, 15, 3, 8, 323455, tzinfo=datetime.timezone.utc), tickType=9, price=313.4, size=0), TickData(time=datetime.datetime(2021, 9, 29, 15, 3, 8, 323455, tzinfo=datetime.timezone.utc), tickType=14, price=319.01, size=0), TickData(time=datetime.datetime(2021, 9, 29, 15, 3, 8, 323455, tzinfo=datetime.timezone.utc), tickType=49, price=0.0, size=0)])
1 async tick Stock: 3338.06 Ticker(contract=Stock(symbol='AMZN', exchange='SMART', currency='USD'), time=datetime.datetime(2021, 9, 29, 15, 3, 8, 323455, tzinfo=datetime.timezone.utc), bid=3337.96, bidSize=1, ask=3339.96, askSize=4, last=3338.06, lastSize=1, volume=8897, open=3322.11, high=3351.3, low=3317.86, close=3315.96, halted=0.0, ticks=[TickData(time=datetime.datetime(2021, 9, 29, 15, 3, 8, 323455, tzinfo=datetime.timezone.utc), tickType=1, price=3337.96, size=1), TickData(time=datetime.datetime(2021, 9, 29, 15, 3, 8, 323455, tzinfo=datetime.timezone.utc), tickType=2, price=3339.96, size=4), TickData(time=datetime.datetime(2021, 9, 29, 15, 3, 8, 323455, tzinfo=datetime.timezone.utc), tickType=4, price=3338.06, size=1), TickData(time=datetime.datetime(2021, 9, 29, 15, 3, 8, 323455, tzinfo=datetime.timezone.utc), tickType=5, price=3338.06, size=1), TickData(time=datetime.datetime(2021, 9, 29, 15, 3, 8, 323455, tzinfo=datetime.timezone.utc), tickType=8, price=-1.0, size=8897), TickData(time=datetime.datetime(2021, 9, 29, 15, 3, 8, 323455, tzinfo=datetime.timezone.utc), tickType=6, price=3351.3, size=0), TickData(time=datetime.datetime(2021, 9, 29, 15, 3, 8, 323455, tzinfo=datetime.timezone.utc), tickType=7, price=3317.86, size=0), TickData(time=datetime.datetime(2021, 9, 29, 15, 3, 8, 323455, tzinfo=datetime.timezone.utc), tickType=9, price=3315.96, size=0), TickData(time=datetime.datetime(2021, 9, 29, 15, 3, 8, 323455, tzinfo=datetime.timezone.utc), tickType=14, price=3322.11, size=0), TickData(time=datetime.datetime(2021, 9, 29, 15, 3, 8, 323455, tzinfo=datetime.timezone.utc), tickType=49, price=0.0, size=0)])
2 async tick Stock: 2729.48 Ticker(contract=Stock(symbol='GOOGL', exchange='SMART', currency='USD'), time=datetime.datetime(2021, 9, 29, 15, 3, 8, 323455, tzinfo=datetime.timezone.utc), bid=2728.45, bidSize=1, ask=2729.85, askSize=2, last=2729.48, lastSize=1, volume=4194, open=2739.6, high=2743.02, low=2713.05, close=2716.6, halted=0.0, ticks=[TickData(time=datetime.datetime(2021, 9, 29, 15, 3, 8, 323455, tzinfo=datetime.timezone.utc), tickType=1, price=2728.45, size=1), TickData(time=datetime.datetime(2021, 9, 29, 15, 3, 8, 323455, tzinfo=datetime.timezone.utc), tickType=2, price=2729.85, size=2), TickData(time=datetime.datetime(2021, 9, 29, 15, 3, 8, 323455, tzinfo=datetime.timezone.utc), tickType=4, price=2729.48, size=1), TickData(time=datetime.datetime(2021, 9, 29, 15, 3, 8, 323455, tzinfo=datetime.timezone.utc), tickType=5, price=2729.48, size=1), TickData(time=datetime.datetime(2021, 9, 29, 15, 3, 8, 323455, tzinfo=datetime.timezone.utc), tickType=8, price=-1.0, size=4194), TickData(time=datetime.datetime(2021, 9, 29, 15, 3, 8, 323455, tzinfo=datetime.timezone.utc), tickType=6, price=2743.02, size=0), TickData(time=datetime.datetime(2021, 9, 29, 15, 3, 8, 323455, tzinfo=datetime.timezone.utc), tickType=7, price=2713.05, size=0), TickData(time=datetime.datetime(2021, 9, 29, 15, 3, 8, 323455, tzinfo=datetime.timezone.utc), tickType=9, price=2716.6, size=0), TickData(time=datetime.datetime(2021, 9, 29, 15, 3, 8, 323455, tzinfo=datetime.timezone.utc), tickType=14, price=2739.6, size=0), TickData(time=datetime.datetime(2021, 9, 29, 15, 3, 8, 323455, tzinfo=datetime.timezone.utc), tickType=49, price=0.0, size=0)])
3 async tick Stock: 208.54 Ticker(contract=Stock(symbol='NVDA', exchange='SMART', currency='USD'), time=datetime.datetime(2021, 9, 29, 15, 3, 8, 323455, tzinfo=datetime.timezone.utc), bid=208.54, bidSize=3, ask=208.59, askSize=2, last=208.54, lastSize=1, volume=76560, open=209.33, high=210.17, low=207.1, close=206.99, halted=0.0, ticks=[TickData(time=datetime.datetime(2021, 9, 29, 15, 3, 8, 323455, tzinfo=datetime.timezone.utc), tickType=1, price=208.54, size=3), TickData(time=datetime.datetime(2021, 9, 29, 15, 3, 8, 323455, tzinfo=datetime.timezone.utc), tickType=2, price=208.59, size=2), TickData(time=datetime.datetime(2021, 9, 29, 15, 3, 8, 323455, tzinfo=datetime.timezone.utc), tickType=4, price=208.54, size=1), TickData(time=datetime.datetime(2021, 9, 29, 15, 3, 8, 323455, tzinfo=datetime.timezone.utc), tickType=5, price=208.54, size=1), TickData(time=datetime.datetime(2021, 9, 29, 15, 3, 8, 323455, tzinfo=datetime.timezone.utc), tickType=8, price=-1.0, size=76560), TickData(time=datetime.datetime(2021, 9, 29, 15, 3, 8, 323455, tzinfo=datetime.timezone.utc), tickType=6, price=210.17, size=0), TickData(time=datetime.datetime(2021, 9, 29, 15, 3, 8, 323455, tzinfo=datetime.timezone.utc), tickType=7, price=207.1, size=0), TickData(time=datetime.datetime(2021, 9, 29, 15, 3, 8, 323455, tzinfo=datetime.timezone.utc), tickType=9, price=206.99, size=0), TickData(time=datetime.datetime(2021, 9, 29, 15, 3, 8, 323455, tzinfo=datetime.timezone.utc), tickType=14, price=209.33, size=0), TickData(time=datetime.datetime(2021, 9, 29, 15, 3, 8, 323455, tzinfo=datetime.timezone.utc), tickType=49, price=0.0, size=0)])
0 async tick Stock: 282.18 Ticker(contract=Stock(symbol='BNTX', exchange='SMART', currency='USD'), time=datetime.datetime(2021, 9, 29, 15, 3, 8, 329196, tzinfo=datetime.timezone.utc), bid=282.03, bidSize=4, ask=282.34, askSize=2, last=282.18, lastSize=1, volume=13112, open=287.0, high=292.81, low=281.2, close=276.52, halted=0.0, ticks=[TickData(time=datetime.datetime(2021, 9, 29, 15, 3, 8, 329196, tzinfo=datetime.timezone.utc), tickType=1, price=282.03, size=4), TickData(time=datetime.datetime(2021, 9, 29, 15, 3, 8, 329196, tzinfo=datetime.timezone.utc), tickType=2, price=282.34, size=2), TickData(time=datetime.datetime(2021, 9, 29, 15, 3, 8, 329196, tzinfo=datetime.timezone.utc), tickType=4, price=282.18, size=1), TickData(time=datetime.datetime(2021, 9, 29, 15, 3, 8, 329196, tzinfo=datetime.timezone.utc), tickType=5, price=282.18, size=1), TickData(time=datetime.datetime(2021, 9, 29, 15, 3, 8, 329196, tzinfo=datetime.timezone.utc), tickType=8, price=-1.0, size=13112), TickData(time=datetime.datetime(2021, 9, 29, 15, 3, 8, 329196, tzinfo=datetime.timezone.utc), tickType=6, price=292.81, size=0), TickData(time=datetime.datetime(2021, 9, 29, 15, 3, 8, 329196, tzinfo=datetime.timezone.utc), tickType=7, price=281.2, size=0), TickData(time=datetime.datetime(2021, 9, 29, 15, 3, 8, 329196, tzinfo=datetime.timezone.utc), tickType=9, price=276.52, size=0), TickData(time=datetime.datetime(2021, 9, 29, 15, 3, 8, 329196, tzinfo=datetime.timezone.utc), tickType=14, price=287.0, size=0), TickData(time=datetime.datetime(2021, 9, 29, 15, 3, 8, 329196, tzinfo=datetime.timezone.utc), tickType=49, price=0.0, size=0)])
Now while I can fill these fields bit by bit in my Rust Tickers
struct, as it happens in Python, the issue I have is the time stamps.
I only get a tick_type
lastTimestamp
via my wrapper's tick_string()
every now and then while other data like bidPrice
updates at much higher frequency. Do I just use the current UTC time to create them myself? They would obviously be off my some delta, depending on a lot of factors.
The Python code sample doesn't even list any explicit subscriptions when calling reqMktData()
.
What am I missing here?
I notice that you have actually left comments documenting each function, but they are not in standard format (see here) and hence they don't show up in the automatically generated documentation when I ran cargo doc
.
Is this intended? If not I wouldn't mind helping to reformat the comments so that the documentation can be automatically generated.
Thank you for your work,
I just simply imported your crate and used this demo code you provided:
It placed an order of 200 shares of BMW, now I know you like the cars but I can't find in this code where this is the case, I am running TWS version Build 10.17 why is this sending orders out of the blue? When just running the below? Thanks!
use std::sync::{Arc, Mutex};
use std::thread;
use std::time::Duration;
use twsapi::core::client::EClient;
use twsapi::core::errors::IBKRApiLibError;
use twsapi::core::streamer::{Streamer, TcpStreamer};
use twsapi::examples::test_helpers::TestWrapper;
fn main() -> Result<(), IBKRApiLibError> {
let wrapper = Arc::new(Mutex::new(TestWrapper::<TcpStreamer>::new()));
let app = Arc::new(Mutex::new(EClient::new(wrapper.clone())));
println!("getting connection...");
wrapper.lock().expect("Wrapper mutex was poisoned").client = Option::from(app.clone());
//use port 7497 for TWS or 4002 for IB Gateway, depending on the port you have set
app.lock()
.expect("EClient mutex was poisoned")
.connect("127.0.0.1", 7496, 0)?;
//4002
thread::sleep(Duration::new(18600, 0));
Ok(())
}
Currently, tests are manual against a running instance of IB Gateway. Add unit and integration tests that run automatically after building
The wrapper trait relies on the version of BigDecimal bundled with IBKR-API-Rust.
BigDecimal should be re-exported so that users can implement this trait. Without a re-export, it is difficult for users to ensure they have a matching BigDecimal library version.
I'm using the Rust wrapper to the IB Gateway API
(https://github.com/sparkstartconsulting/IBKR-API-Rust).
Everything works dandy if I run the gateway locally, on my machine, and
connect via IP (127.0.0.1).
However, when I try to connect to any remote gateway host, I get:
INFO - Connecting
INFO - socket either closed or broken, disconnecting
Error: IO error: Socket is not connected (os error 57)
Any ideas what I could be missing?
Cheers,
Hello. When using your library, I receive the following message:
Your API version does not support fractional size rules. Please upgrade to a minimum version 163
I'm subscribing the following:
let contract = {
let mut c = Contract::default();
c.symbol = "eth".to_string();
c.sec_type = "crypto".to_string();
c.currency = "usd".to_string();
c.exchange = "paxos".to_string();
c
};
app.lock().await.req_tick_by_tick_data(req_id, &contract, TickByTickType::AllLast, 0, false)?;
At the /core/decoder.rs, process_error_message uses print function which is frequently line-buffered by default, and the error info would be delayed to display at the io::stdout.
And io::stdout.flush() or switch to println(),
or just remove that as it can be catch at wrapper callback
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.