update
This commit is contained in:
@@ -0,0 +1,346 @@
|
||||
|
||||
> 策略名称
|
||||
|
||||
C++商品期货高频交易策略之Penny Jump
|
||||
|
||||
> 策略作者
|
||||
|
||||
Zero
|
||||
|
||||
> 策略描述
|
||||
|
||||
> 高频交易分类
|
||||
|
||||
* 以最快的速度抹平市场的一切不合理现象, 常见于炒单与高频套利, 主动出击, 拼速度
|
||||
* 提前在不合理的价位埋伏并跟随市场快速移动, 常见于市商策略, 守株待兔, 拼知识面
|
||||
* 其它各种作者只学习了理论没有实盘, 先不讲
|
||||
|
||||
> 动手实现
|
||||
|
||||
* 观察盘口的概率极低的不合理现像并做出相应策略进行测试更改
|
||||
* 如果逻辑复杂, 就需要利用现有的数学知识尽可能的用模型描述不合理现象的本质, 尽量减少拟合
|
||||
* 必须用可以见量成交又可以见价成交的回测模型去验证, 发明者量化目前是唯一支持这两种回测模式的平台
|
||||
* 平台的C++例子很少, 这里就用C++写本策略, 方便大家学习
|
||||
|
||||
> 策略原理
|
||||
|
||||
正常情况下商品期货的盘口买一卖一差价只有一跳, 偶尔会有两跳,三跳基本很少见, 主力合约交易比较频繁, 这种现像发生后瞬间消失, 难以有下手机会, 我们就把精力放到次主力合约上面,比如MA001为主力, MA909为次主力时, MA909盘口下面这种情况
|
||||
|
||||

|
||||
|
||||
卖一为2225量551, 买一2223量565, 向下看几秒, 出现这种情况后, 几个tick推送后消失, 这种情况,我们视为市场的自我纠正, 我们要做的就是赶在市场主动纠正前, 杀进去, 这种逻辑看人工去盯盘是天方夜谈, 因为商品期货盘口差价两跳的情况级少出现, 三跳最安全, 但三跳极少出现, 导致交易频率太低, 意义不大, 接下来, 我们观察盘口之前卖一买一与现在两跳时买一卖一的区别, 去填补盘口差价空隙, 如果速度够快, 就可以排在委托单的最前位置, 做为Maker以最快的速度成交后反手卖出, 持仓时间很短, 有了这个交易逻辑, 实现为策略以后, 以MA909为例, 实盘测试推荐易盛而非CTP接口, 易盛仓位与资金变化是推送机制, 非常适合高频
|
||||
|
||||
> 回测结果
|
||||
|
||||

|
||||
|
||||
> 交易逻辑
|
||||
|
||||

|
||||
|
||||
> 必看声明
|
||||
|
||||
为满足对高频交易的好奇心, 为了更明显的看到结果, 此策略回测手续费设定为0, 实现了一个简单的拼速度逻辑, 想要覆盖手续费实现盈利, 实盘需做更多优化, 仅凭这个简单的逻辑很难致胜, 要考虑更多操作, 比如锁仓(降低平今手续费), 利用定单薄流进行短期预测提高胜率, 再加上交易所手续费返还, 从而实现一个可持久盈利的策略, 关于高频交易的书籍很多, 希望大家多去思考,多去实盘,而不是只停留在原理上.
|
||||
|
||||
> 关于我们
|
||||
|
||||
发明者量化是一个纯技术驱动的团队, 为量化交易爱好者提供了一个高可用的回测机制, 我们的回测机制是真实的模拟了一个交易所的存在, 而不是简单的见价撮合, 希望用户能够利用到平台的优点更好的去发挥自己的能力
|
||||
|
||||
> 策略参数
|
||||
|
||||
|
||||
|
||||
|参数|默认值|描述|
|
||||
|----|----|----|
|
||||
|Symbol|MA909|品种|
|
||||
|RetryMax|true|重试最多|
|
||||
|ProfitTick|true|利润跳数|
|
||||
|TolerateTick|5|容忍跳数|
|
||||
|
||||
|
||||
> 源码 (cpp)
|
||||
|
||||
``` cpp
|
||||
/*backtest
|
||||
start: 2019-08-19 09:00:00
|
||||
end: 2019-08-23 15:00:00
|
||||
period: 1d
|
||||
exchanges: [{"eid":"Futures_CTP","currency":"FUTURES","balance":10000,"minfee":0,"fee":[0,0]}]
|
||||
mode: 1
|
||||
*/
|
||||
|
||||
enum State {
|
||||
STATE_NA,
|
||||
STATE_IDLE,
|
||||
STATE_HOLD_LONG,
|
||||
STATE_HOLD_SHORT,
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
double bidPrice;
|
||||
double bidAmount;
|
||||
double askPrice;
|
||||
double askAmount;
|
||||
} Book;
|
||||
|
||||
class HFT {
|
||||
public:
|
||||
HFT() {
|
||||
_tradingDay = getTradingWeekDay();
|
||||
Log("current trading weekday", _tradingDay);
|
||||
}
|
||||
int getTradingWeekDay() {
|
||||
int seconds = Unix() + 28800;
|
||||
int hour = (seconds/3600)%24;
|
||||
int weekDay = (seconds/(60*60*24))%7+4;
|
||||
if (hour > 20) {
|
||||
weekDay += 1;
|
||||
}
|
||||
return weekDay;
|
||||
}
|
||||
State getState() {
|
||||
auto orders = exchange.GetOrders();
|
||||
if (!orders.Valid || orders.size() == 2) {
|
||||
return STATE_NA;
|
||||
}
|
||||
|
||||
bool foundCover = false;
|
||||
for (auto &order : orders) {
|
||||
if (order.Id == _coverId) {
|
||||
if ((order.Type == ORDER_TYPE_BUY && order.Price < _book.bidPrice - _toleratePrice) ||
|
||||
(order.Type == ORDER_TYPE_SELL && order.Price > _book.askPrice + _toleratePrice)) {
|
||||
exchange.CancelOrder(order.Id, "Cancel Cover Order");
|
||||
_countCancel++;
|
||||
_countRetry++;
|
||||
} else {
|
||||
foundCover = true;
|
||||
}
|
||||
} else {
|
||||
exchange.CancelOrder(order.Id);
|
||||
_countCancel++;
|
||||
}
|
||||
}
|
||||
|
||||
if (foundCover) {
|
||||
return STATE_NA;
|
||||
}
|
||||
|
||||
auto positions = exchange.GetPosition();
|
||||
if (!positions.Valid) {
|
||||
return STATE_NA;
|
||||
}
|
||||
|
||||
for (auto &pos : positions) {
|
||||
if (pos.ContractType == Symbol) {
|
||||
_holdPrice = pos.Price;
|
||||
_holdAmount = pos.Amount;
|
||||
_holdType = pos.Type;
|
||||
return pos.Type == PD_LONG || pos.Type == PD_LONG_YD ? STATE_HOLD_LONG : STATE_HOLD_SHORT;
|
||||
}
|
||||
}
|
||||
return STATE_IDLE;
|
||||
}
|
||||
|
||||
void stop() {
|
||||
Log(exchange.GetOrders());
|
||||
Log(exchange.GetPosition());
|
||||
Log("Stop");
|
||||
}
|
||||
|
||||
bool Loop() {
|
||||
if (exchange.IO("status") == 0) {
|
||||
LogStatus(_D(), "Server not connect ....");
|
||||
Sleep(1000);
|
||||
return true;
|
||||
}
|
||||
if (_initBalance == 0) {
|
||||
_initBalance = _C(exchange.GetAccount).Balance;
|
||||
}
|
||||
auto day = getTradingWeekDay();
|
||||
if (day != _tradingDay) {
|
||||
_tradingDay = day;
|
||||
_countCancel = 0;
|
||||
}
|
||||
|
||||
if (_ct.is_null()) {
|
||||
Log(_D(), "subscribe", Symbol);
|
||||
_ct = exchange.SetContractType(Symbol);
|
||||
if (!_ct.is_null()) {
|
||||
auto obj = _ct["Commodity"]["CommodityTickSize"];
|
||||
int volumeMultiple = 1;
|
||||
if (obj.is_null()) { // CTP
|
||||
obj = _ct["PriceTick"];
|
||||
volumeMultiple = _ct["VolumeMultiple"];
|
||||
_exchangeId = _ct["ExchangeID"];
|
||||
} else { // Esunny
|
||||
volumeMultiple = _ct["Commodity"]["ContractSize"];
|
||||
_exchangeId = _ct["Commodity"]["ExchangeNo"];
|
||||
}
|
||||
if (obj.is_null() || obj <= 0) {
|
||||
Panic("PriceTick not found");
|
||||
}
|
||||
if (_priceTick < 1) {
|
||||
exchange.SetPrecision(1, 0);
|
||||
}
|
||||
_priceTick = double(obj);
|
||||
_toleratePrice = _priceTick * TolerateTick;
|
||||
_ins = _ct["InstrumentID"];
|
||||
|
||||
Log(_ins, _exchangeId, "PriceTick:", _priceTick, "VolumeMultiple:", volumeMultiple);
|
||||
}
|
||||
Sleep(1000);
|
||||
return true;
|
||||
}
|
||||
// Check orders and positions to set state
|
||||
auto depth = exchange.GetDepth();
|
||||
if (!depth.Valid) {
|
||||
LogStatus(_D(), "Market not ready");
|
||||
Sleep(1000);
|
||||
return true;
|
||||
}
|
||||
_countTick++;
|
||||
_preBook = _book;
|
||||
_book.bidPrice = depth.Bids[0].Price;
|
||||
_book.bidAmount = depth.Bids[0].Amount;
|
||||
_book.askPrice = depth.Asks[0].Price;
|
||||
_book.askAmount = depth.Asks[0].Amount;
|
||||
// _book not init
|
||||
if (_preBook.bidAmount == 0) {
|
||||
return true;
|
||||
}
|
||||
auto st = getState();
|
||||
|
||||
LogStatus(_D(), _ins, "State:", st,
|
||||
"Ask:", depth.Asks[0].Price, depth.Asks[0].Amount,
|
||||
"Bid:", depth.Bids[0].Price, depth.Bids[0].Amount,
|
||||
"Cancel:", _countCancel,
|
||||
"Tick:", _countTick);
|
||||
|
||||
bool forceCover = _countRetry >= _retryMax;
|
||||
if (st == STATE_IDLE) {
|
||||
if (_holdAmount > 0) {
|
||||
if (_countRetry > 0) {
|
||||
_countLoss++;
|
||||
} else {
|
||||
_countWin++;
|
||||
}
|
||||
auto account = exchange.GetAccount();
|
||||
if (account.Valid) {
|
||||
LogProfit(_N(account.Balance+account.FrozenBalance-_initBalance, 2), "Win:", _countWin, "Loss:", _countLoss);
|
||||
}
|
||||
}
|
||||
_countRetry = 0;
|
||||
_holdAmount = 0;
|
||||
|
||||
if (_countCancel > _cancelMax) {
|
||||
Log("Cancel Exceed", _countCancel);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool canDo = false;
|
||||
if (abs(_book.bidPrice - _book.askPrice) > _priceTick * 1) {
|
||||
canDo = true;
|
||||
}
|
||||
if (!canDo) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
auto bidPrice = depth.Bids[0].Price;
|
||||
auto askPrice = depth.Asks[0].Price;
|
||||
auto bidAmount = 1.0;
|
||||
auto askAmount = 1.0;
|
||||
|
||||
if (_preBook.bidPrice > _book.bidPrice && _book.askAmount < _book.bidAmount) {
|
||||
bidPrice += _priceTick;
|
||||
bidAmount = 2;
|
||||
} else if (_preBook.askPrice < _book.askPrice && _book.bidAmount < _book.askAmount) {
|
||||
askPrice -= _priceTick;
|
||||
askAmount = 2;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
Log(_book.bidPrice, _book.bidAmount, _book.askPrice, _book.askAmount);
|
||||
exchange.SetDirection("buy");
|
||||
exchange.Buy(bidPrice, bidAmount);
|
||||
exchange.SetDirection("sell");
|
||||
exchange.Sell(askPrice, askAmount);
|
||||
} else if (st == STATE_HOLD_LONG) {
|
||||
exchange.SetDirection((_holdType == PD_LONG && _exchangeId == "SHFE") ? "closebuy_today" : "closebuy");
|
||||
auto sellPrice = depth.Asks[0].Price;
|
||||
if (sellPrice > _holdPrice) {
|
||||
Log(_holdPrice, "Hit #ff0000");
|
||||
sellPrice = _holdPrice + ProfitTick;
|
||||
} else if (sellPrice < _holdPrice) {
|
||||
forceCover = true;
|
||||
}
|
||||
if (forceCover) {
|
||||
Log("StopLoss");
|
||||
}
|
||||
_coverId = exchange.Sell(forceCover ? depth.Bids[0].Price : sellPrice, _holdAmount);
|
||||
if (!_coverId.Valid) {
|
||||
return false;
|
||||
}
|
||||
} else if (st == STATE_HOLD_SHORT) {
|
||||
exchange.SetDirection((_holdType == PD_SHORT && _exchangeId == "SHFE") ? "closesell_today" : "closesell");
|
||||
auto buyPrice = depth.Bids[0].Price;
|
||||
if (buyPrice < _holdPrice) {
|
||||
Log(_holdPrice, "Hit #ff0000");
|
||||
buyPrice = _holdPrice - ProfitTick;
|
||||
} else if (buyPrice > _holdPrice) {
|
||||
forceCover = true;
|
||||
}
|
||||
if (forceCover) {
|
||||
Log("StopLoss");
|
||||
}
|
||||
_coverId = exchange.Buy(forceCover ? depth.Asks[0].Price : buyPrice, _holdAmount);
|
||||
if (!_coverId.Valid) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
private:
|
||||
double _holdPrice = 0;
|
||||
double _holdAmount = 0;
|
||||
int _holdType;
|
||||
int _countTick = 0;
|
||||
int _countRetry = 0;
|
||||
int _countCancel = 0;
|
||||
int _period = 20;
|
||||
int _tradingDay = 0;
|
||||
double _initBalance;
|
||||
const int _retryMax = RetryMax;
|
||||
const int _cancelMax = 300;
|
||||
|
||||
int _countLoss = 0;
|
||||
int _countWin = 0;
|
||||
|
||||
json _ct;
|
||||
string _ins;
|
||||
string _exchangeId;
|
||||
double _priceTick;
|
||||
double _toleratePrice;
|
||||
Book _book;
|
||||
Book _preBook;
|
||||
TId _coverId;
|
||||
};
|
||||
|
||||
|
||||
void main() {
|
||||
LogReset();
|
||||
SetErrorFilter("ready|timeout");
|
||||
Log("Init OK");
|
||||
HFT hft;
|
||||
while (hft.Loop());
|
||||
Log("Exit");
|
||||
}
|
||||
```
|
||||
|
||||
> 策略出处
|
||||
|
||||
https://www.fmz.com/strategy/163427
|
||||
|
||||
> 更新时间
|
||||
|
||||
2019-08-24 12:23:42
|
||||
+299
@@ -0,0 +1,299 @@
|
||||
|
||||
> 策略名称
|
||||
|
||||
HFT strategy
|
||||
|
||||
> 策略作者
|
||||
|
||||
Hukybo
|
||||
|
||||
> 策略描述
|
||||
|
||||
策略回测什么才是最重要的?速度?花里胡哨的绩效指标?答案是准确!是的,回测的目的是为了验证策略的逻辑和可行性。这也是回测本身的意义,其他都是次要的。一个能够真正反映策略在历史数据上的回测才具有参考价值,那些看似完美的回测曲线讲故事可以,实盘它不行。
|
||||
[点击阅读更多内容](https://www.fmz.com/bbs-topic/4195)
|
||||
|
||||
> 策略参数
|
||||
|
||||
|
||||
|
||||
|参数|默认值|描述|
|
||||
|----|----|----|
|
||||
|Symbol|MA909|品种|
|
||||
|RetryMax|3|重试最多|
|
||||
|ProfitTick|5|利润调数|
|
||||
|
||||
|
||||
> 源码 (cpp)
|
||||
|
||||
``` cpp
|
||||
/*backtest
|
||||
start: 2019-07-12 09:00:00
|
||||
end: 2019-07-12 15:00:00
|
||||
period: 1d
|
||||
exchanges: [{"eid":"Futures_CTP","currency":"FUTURES"}]
|
||||
mode: 1
|
||||
*/
|
||||
|
||||
enum State {
|
||||
STATE_NA,
|
||||
STATE_IDLE,
|
||||
STATE_HOLD_LONG,
|
||||
STATE_HOLD_SHORT,
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
double bidPrice;
|
||||
double bidAmount;
|
||||
double askPrice;
|
||||
double askAmount;
|
||||
} Book;
|
||||
|
||||
class HFT {
|
||||
public:
|
||||
State getState() {
|
||||
auto orders = exchange.GetOrders();
|
||||
if (!orders.Valid || orders.size() == 2) {
|
||||
return STATE_NA;
|
||||
}
|
||||
|
||||
bool foundCover = false;
|
||||
for (auto &order : orders) {
|
||||
if (order.Id == _coverId) {
|
||||
if ((order.Type == ORDER_TYPE_BUY && order.Price < _book.bidPrice - _slidePrice) ||
|
||||
(order.Type == ORDER_TYPE_SELL && order.Price > _book.askPrice + _slidePrice)) {
|
||||
exchange.CancelOrder(order.Id, "Cancel Cover Order");
|
||||
_countCancel++;
|
||||
_countRetry++;
|
||||
} else {
|
||||
foundCover = true;
|
||||
}
|
||||
} else {
|
||||
exchange.CancelOrder(order.Id);
|
||||
_countCancel++;
|
||||
}
|
||||
}
|
||||
|
||||
if (foundCover) {
|
||||
return STATE_NA;
|
||||
}
|
||||
|
||||
auto positions = exchange.GetPosition();
|
||||
if (!positions.Valid) {
|
||||
return STATE_NA;
|
||||
}
|
||||
|
||||
for (auto &pos : positions) {
|
||||
if (pos.ContractType == Symbol) {
|
||||
_holdPrice = pos.Price;
|
||||
_holdAmount = pos.Amount;
|
||||
_holdType = pos.Type;
|
||||
return pos.Type == PD_LONG || pos.Type == PD_LONG_YD ? STATE_HOLD_LONG : STATE_HOLD_SHORT;
|
||||
}
|
||||
}
|
||||
return STATE_IDLE;
|
||||
}
|
||||
|
||||
void stop() {
|
||||
Log(exchange.GetOrders());
|
||||
Log(exchange.GetPosition());
|
||||
Log("Stop");
|
||||
}
|
||||
|
||||
bool Loop() {
|
||||
if (exchange.IO("status") == 0) {
|
||||
LogStatus(_D(), "Server not connect ....");
|
||||
Sleep(1000);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_ct.is_null()) {
|
||||
Log(_D(), "subscribe", Symbol);
|
||||
_ct = exchange.SetContractType(Symbol);
|
||||
if (!_ct.is_null()) {
|
||||
auto obj = _ct["Commodity"]["CommodityTickSize"];
|
||||
int volumeMultiple = 1;
|
||||
if (obj.is_null()) {
|
||||
obj = _ct["PriceTick"];
|
||||
volumeMultiple = _ct["VolumeMultiple"];
|
||||
_exchangeId = _ct["ExchangeID"];
|
||||
} else {
|
||||
volumeMultiple = _ct["Commodity"]["ContractSize"];
|
||||
_exchangeId = _ct["Commodity"]["ExchangeNo"];
|
||||
}
|
||||
if (obj.is_null() || obj <= 0) {
|
||||
Panic("PriceTick not found");
|
||||
}
|
||||
if (_priceTick < 1) {
|
||||
exchange.SetPrecision(1, 0);
|
||||
}
|
||||
_priceTick = double(obj);
|
||||
_slidePrice = _priceTick * ProfitTick;
|
||||
_ins = _ct["InstrumentID"];
|
||||
|
||||
Log(_ins, _exchangeId, "PriceTick:", _priceTick, "VolumeMultiple:", volumeMultiple);
|
||||
}
|
||||
Sleep(1000);
|
||||
return true;
|
||||
}
|
||||
// Check orders and positions to set state
|
||||
auto depth = exchange.GetDepth();
|
||||
if (!depth.Valid) {
|
||||
LogStatus(_D(), "Market not ready");
|
||||
Sleep(1000);
|
||||
return true;
|
||||
}
|
||||
_countTick++;
|
||||
// _book not init
|
||||
if (_book.bidAmount == 0) {
|
||||
return true;
|
||||
}
|
||||
_preBook = _book;
|
||||
_book.bidPrice = depth.Bids[0].Price;
|
||||
_book.bidAmount = depth.Bids[0].Amount;
|
||||
_book.askPrice = depth.Asks[0].Price;
|
||||
_book.askAmount = depth.Asks[0].Amount;
|
||||
|
||||
double weight = depth.Asks[0].Price + depth.Bids[0].Price;
|
||||
_priceHis.push_back(weight);
|
||||
if (_priceHis.size() > 100) {
|
||||
_priceHis.erase(_priceHis.begin());
|
||||
}
|
||||
|
||||
auto st = getState();
|
||||
|
||||
LogStatus(_D(), _ins, "State:", st,
|
||||
"Ask:", depth.Asks[0].Price, depth.Asks[0].Amount,
|
||||
"Bid:", depth.Bids[0].Price, depth.Bids[0].Amount,
|
||||
"Cancel:", _countCancel,
|
||||
"Tick:", _countTick);
|
||||
Log(depth.Asks[0], depth.Bids[0]);
|
||||
|
||||
bool forceCover = _countRetry >= _retryMax;
|
||||
if (st == STATE_IDLE) {
|
||||
if (_holdAmount > 0) {
|
||||
if (_countRetry > 0) {
|
||||
_countLoss++;
|
||||
} else {
|
||||
_countWin++;
|
||||
}
|
||||
auto account = exchange.GetAccount();
|
||||
if (account.Valid) {
|
||||
LogProfit(_N(account.Balance+account.FrozenBalance, 2), "Win:", _countWin, "Loss:", _countLoss);
|
||||
}
|
||||
}
|
||||
_countRetry = 0;
|
||||
_holdAmount = 0;
|
||||
|
||||
if (_countCancel > _cancelMax) {
|
||||
Log("Cancel Exceed", _countCancel);
|
||||
return false;
|
||||
}
|
||||
if (IsVirtual()) {
|
||||
if (_priceHis.size() < _period) {
|
||||
LogStatus("calc history", _priceHis.size());
|
||||
return true;
|
||||
}
|
||||
auto arr = talib.STDDEV(_priceHis, _period);
|
||||
double diff = abs(weight - _priceHis[_priceHis.size() - 2]);
|
||||
if (diff < arr[arr.size()-1]) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
bool canDo = false;
|
||||
if (_book.bidPrice == _preBook.bidPrice && _book.bidAmount > 50 && abs(_preBook.bidAmount-_book.bidAmount) > 50) {
|
||||
canDo = true;
|
||||
Log("Bid Amount", _preBook.bidAmount, "->", _book.bidAmount);
|
||||
}
|
||||
if (_book.askPrice == _preBook.askPrice && _book.askAmount > 50 && abs(_preBook.askAmount-_book.askAmount) > 50) {
|
||||
canDo = true;
|
||||
Log("Ask Amount", _preBook.askAmount, "->", _book.askAmount);
|
||||
}
|
||||
if (!canDo) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
exchange.SetDirection("buy");
|
||||
exchange.Buy(depth.Bids[0].Price, 1);
|
||||
exchange.SetDirection("sell");
|
||||
exchange.Sell(depth.Asks[0].Price, 1);
|
||||
} else if (st == STATE_HOLD_LONG) {
|
||||
|
||||
exchange.SetDirection((_holdType == PD_LONG && _exchangeId == "SHFE") ? "closebuy_today" : "closebuy");
|
||||
auto sellPrice = depth.Asks[0].Price;
|
||||
if (sellPrice > _holdPrice) {
|
||||
Log(_holdPrice, "Hit #ff0000");
|
||||
//sellPrice = depth.Asks[0].Price + _slidePrice;
|
||||
} else if (sellPrice < _holdPrice) {
|
||||
//forceCover = true;
|
||||
}
|
||||
if (forceCover) {
|
||||
Log("StopLoss");
|
||||
}
|
||||
_coverId = exchange.Sell(forceCover ? depth.Bids[0].Price : sellPrice, _holdAmount);
|
||||
if (!_coverId.Valid) {
|
||||
return false;
|
||||
}
|
||||
} else if (st == STATE_HOLD_SHORT) {
|
||||
|
||||
exchange.SetDirection((_holdType == PD_SHORT && _exchangeId == "SHFE") ? "closesell_today" : "closesell");
|
||||
auto buyPrice = depth.Bids[0].Price;
|
||||
if (buyPrice < _holdPrice) {
|
||||
Log(_holdPrice, "Hit #ff0000");
|
||||
} else if (buyPrice > _holdPrice) {
|
||||
//forceCover = true;
|
||||
}
|
||||
if (forceCover) {
|
||||
Log("StopLoss");
|
||||
}
|
||||
_coverId = exchange.Buy(forceCover ? depth.Asks[0].Price : buyPrice, _holdAmount);
|
||||
if (!_coverId.Valid) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
private:
|
||||
double _holdPrice = 0;
|
||||
double _holdAmount = 0;
|
||||
int _holdType;
|
||||
int _countTick = 0;
|
||||
int _countRetry = 0;
|
||||
int _countCancel = 0;
|
||||
int _period = 20;
|
||||
const int _retryMax = RetryMax;
|
||||
const int _cancelMax = 300;
|
||||
|
||||
int _countLoss = 0;
|
||||
int _countWin = 0;
|
||||
|
||||
json _ct;
|
||||
string _ins;
|
||||
string _exchangeId;
|
||||
double _priceTick;
|
||||
double _slidePrice;
|
||||
vector<double> _priceHis;
|
||||
Book _book;
|
||||
Book _preBook;
|
||||
TId _coverId;
|
||||
};
|
||||
|
||||
|
||||
void main() {
|
||||
LogReset();
|
||||
SetErrorFilter("ready|timeout");
|
||||
Log("Init OK");
|
||||
HFT hft;
|
||||
while (hft.Loop());
|
||||
Log("Exit");
|
||||
}
|
||||
|
||||
|
||||
```
|
||||
|
||||
> 策略出处
|
||||
|
||||
https://www.fmz.com/strategy/162372
|
||||
|
||||
> 更新时间
|
||||
|
||||
2019-08-23 16:05:13
|
||||
@@ -0,0 +1,150 @@
|
||||
|
||||
> 策略名称
|
||||
|
||||
MultDualThrust(教学)
|
||||
|
||||
> 策略作者
|
||||
|
||||
小小梦
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
> 源码 (javascript)
|
||||
|
||||
``` javascript
|
||||
/*
|
||||
## 商品期货 多品种 Dual Thrust 策略
|
||||
|
||||
*/
|
||||
|
||||
var _Symbols = [
|
||||
{
|
||||
ContractTypeName : "rb1910",
|
||||
NPeriod : 4,
|
||||
Ks : 0.5,
|
||||
Kx : 0.5,
|
||||
AmountOP : 1,
|
||||
},
|
||||
{
|
||||
ContractTypeName : "i1909",
|
||||
NPeriod : 4,
|
||||
Ks : 0.5,
|
||||
Kx : 0.5,
|
||||
AmountOP : 1,
|
||||
},
|
||||
{
|
||||
ContractTypeName : "pp1909",
|
||||
NPeriod : 4,
|
||||
Ks : 0.5,
|
||||
Kx : 0.5,
|
||||
AmountOP : 1,
|
||||
},
|
||||
{
|
||||
ContractTypeName : "MA909",
|
||||
NPeriod : 4,
|
||||
Ks : 0.5,
|
||||
Kx : 0.5,
|
||||
AmountOP : 1,
|
||||
},
|
||||
]
|
||||
|
||||
var manager = $.NewPositionManager()
|
||||
var STATE_IDLE = 0;
|
||||
var STATE_LONG = 1;
|
||||
var STATE_SHORT = 2;
|
||||
|
||||
function init () {
|
||||
for (var i = 0 ; i < _Symbols.length ; i++) {
|
||||
_Symbols[i].State = STATE_IDLE
|
||||
_Symbols[i].LastBarTime = 0
|
||||
_Symbols[i].UpTrack = 0
|
||||
_Symbols[i].DownTrack = 0
|
||||
}
|
||||
}
|
||||
|
||||
function DualThrustProcess (symbols) {
|
||||
for (var i = 0 ; i < symbols.length ; i++) {
|
||||
var ContractTypeName = symbols[i].ContractTypeName
|
||||
var NPeriod = symbols[i].NPeriod
|
||||
var Ks = symbols[i].Ks
|
||||
var Kx = symbols[i].Kx
|
||||
var AmountOP = symbols[i].AmountOP
|
||||
|
||||
// 首先判断是不是在交易时间
|
||||
if (!$.IsTrading(ContractTypeName)) {
|
||||
continue
|
||||
}
|
||||
|
||||
// 切换为当前 symbol 参数的合约
|
||||
var insDetail = _C(exchange.SetContractType, ContractTypeName)
|
||||
|
||||
// 判断K线长度
|
||||
var records = _C(exchange.GetRecords);
|
||||
if (!records || records.length <= NPeriod) {
|
||||
LogStatus("Calc Bars...");
|
||||
continue
|
||||
}
|
||||
|
||||
var Bar = records[records.length - 1]
|
||||
if (symbols[i].LastBarTime !== Bar.Time) {
|
||||
var HH = TA.Highest(records, NPeriod, 'High')
|
||||
var HC = TA.Highest(records, NPeriod, 'Close')
|
||||
var LL = TA.Lowest(records, NPeriod, 'Low')
|
||||
var LC = TA.Lowest(records, NPeriod, 'Close')
|
||||
var Range = Math.max(HH - LC, HC - LL)
|
||||
|
||||
symbols[i].UpTrack = _N(Bar.Open + (Ks * Range))
|
||||
symbols[i].DownTrack = _N(Bar.Open - (Kx * Range))
|
||||
|
||||
symbols[i].LastBarTime = Bar.Time;
|
||||
}
|
||||
|
||||
if (symbols[i].State === STATE_IDLE || symbols[i].State === STATE_SHORT) {
|
||||
if (Bar.Close >= symbols[i].UpTrack) {
|
||||
if (symbols[i].State !== STATE_IDLE) {
|
||||
Log(ContractTypeName, "平空仓")
|
||||
manager.Cover(ContractTypeName);
|
||||
}
|
||||
Log(ContractTypeName, "开多仓")
|
||||
manager.OpenLong(ContractTypeName, AmountOP);
|
||||
symbols[i].State = STATE_LONG;
|
||||
}
|
||||
}
|
||||
|
||||
if (symbols[i].State === STATE_IDLE || symbols[i].State === STATE_LONG) {
|
||||
if (Bar.Close <= symbols[i].DownTrack) {
|
||||
if (symbols[i].State !== STATE_IDLE) {
|
||||
Log(ContractTypeName, "平多仓")
|
||||
manager.Cover(ContractTypeName);
|
||||
}
|
||||
Log(ContractTypeName, "开空仓")
|
||||
manager.OpenShort(ContractTypeName, AmountOP);
|
||||
symbols[i].State = STATE_SHORT;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function main(){
|
||||
while(true){
|
||||
if(exchange.IO("status")){
|
||||
LogStatus(_D(), "已经连接CTP !")
|
||||
DualThrustProcess(_Symbols)
|
||||
} else {
|
||||
LogStatus(_D(), "未连接CTP !")
|
||||
}
|
||||
Sleep(1000)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
> 策略出处
|
||||
|
||||
https://www.fmz.com/strategy/163212
|
||||
|
||||
> 更新时间
|
||||
|
||||
2019-08-22 15:39:24
|
||||
+497
@@ -0,0 +1,497 @@
|
||||
|
||||
> 策略名称
|
||||
|
||||
OKEx跨期对冲策略
|
||||
|
||||
> 策略作者
|
||||
|
||||
reboting
|
||||
|
||||
> 策略描述
|
||||
|
||||
什么是跨期对冲?
|
||||
所谓跨期套利就是在同一期货品种的不同月份合约上建立数量相等、方向相反的交易头寸,最后以对冲或交割方式结束交易、获得收益的方式。最简单的跨期套利就是买入近期的期货品种,卖出远期的期货品种。比如Okex的BTC次周和当周合约。交割期不同,最多相差3个月。当合约价差出现时,投资者可进行买入一个合约同时卖出另外一个合约,待到价差回归后再进行相应的反向平仓,进而利用价差的合理回归获得利润。
|
||||
|
||||
如何在Okex上进行跨期对冲?
|
||||
okex上当周、次周和季度合约的价格经常会存在价差,如果价差达到或超过一定的阈值,则可以进行跨期对冲,然后在价差消失时进行反向平仓,进而利用价差的合理回归获得利润。比如,BTC当周和次周合约存在价差且当周合约低于次周合约价格,当价差达到设定阈值,投资者可以做多当周合约和做空次周合约(数量一致)进行对冲,等到当周合约和次周合约价差回归正常值时进行相应的反向平仓,获取利润。
|
||||
|
||||
跨期对冲的风险:
|
||||
因为两种合约的交割时间不同,当近期合约强制交割时,价差未能回归则可能出现亏损。
|
||||
|
||||
策略实现的功能和特点:
|
||||
支持Okex跨期对冲
|
||||
支持Okex的当周、次周和季度合约
|
||||
支持Okex的所有合约交易品种(BTC、BCH、EOS、BSV、ETH等等)
|
||||
|
||||
特别注意:此策略需要依赖Botvstools模板库才能运行!
|
||||
请到此处下载模板库:
|
||||
https://www.pcclean.io/45gd
|
||||
(下载zip文件后解压缩会有两个js文件,一个是策略,一个是模板库,请注意区分)
|
||||
|
||||
策略参数说明:
|
||||
https://www.pcclean.io/45gd
|
||||
|
||||
|
||||
|
||||
> 源码 (javascript)
|
||||
|
||||
``` javascript
|
||||
var strategy_version="1.2.0.7(adjust parameters)";
|
||||
|
||||
/*
|
||||
使用说明:
|
||||
1. 请先针对交易所和交易对设置策略参数后再运行此策略。
|
||||
2. fmz中添加交易所: okex期货交易所
|
||||
3. 参数里面contract_min表示单个合约价值,不要随意更改
|
||||
4. 建议okex里面设置为全仓模式,以免出现保证金不足的情况
|
||||
5. 尽量使用okex api v1
|
||||
6. 此策略仅用于学习分享,实盘风险自担。
|
||||
*/
|
||||
|
||||
/********************************************策略参数**********************************/
|
||||
var price_n={Futures_OKCoin_BSV_USD:2};//价格精度设置
|
||||
var num_n={Futures_OKCoin_BSV_USD:0};//数量精度设置
|
||||
var minestbuy={Futures_OKCoin_BSV_USD:1};//最小买入量
|
||||
var price_step={Futures_OKCoin_BSV_USD:0.05};//定价单调整量
|
||||
var contract_min={Futures_OKCoin_BSV_USD:10};//最小合约金额
|
||||
var wait_ms=3000;//重试等待时间(ms)
|
||||
var max_wait_order=10000;//订单等待时间(ms)
|
||||
var margin_lv=10;//杠杆倍数
|
||||
var jiacha_monitor={tw_nw:0.02,tw_qt:0.02,nw_qt:0.02};//开仓差价
|
||||
var hulie_monitor={tw_nw:0.003,tw_qt:0.003,nw_qt:0.003};//忽略的差价
|
||||
var ok_future_target='bsv';//目标合约
|
||||
var keep_risk_rate=10;//保证金率
|
||||
var trade_unit=100;//每次交易多少张
|
||||
var push_notification=true;//微信通知交易机会
|
||||
/********************************************策略参数**********************************/
|
||||
|
||||
|
||||
//全局变量
|
||||
var total_loop=0;
|
||||
|
||||
//主函数
|
||||
function main(){
|
||||
Log("strategy_version="+strategy_version);
|
||||
$.set_params(price_n,num_n,minestbuy,price_step,wait_ms,max_wait_order);
|
||||
|
||||
if (push_notification){
|
||||
Log("策略开始运行!已开启推送。@");
|
||||
}
|
||||
|
||||
while(true){
|
||||
exchange.SetMarginLevel(margin_lv);
|
||||
var exname=exchange.GetName();
|
||||
var currency=exchange.GetCurrency();
|
||||
var account=$.retry_get_account(exchange);
|
||||
var f_orders=_C(exchange.GetOrders);
|
||||
|
||||
exchange.SetContractType("this_week");
|
||||
var tw_depth=_C(exchange.GetDepth);
|
||||
var tw_sell1=tw_depth.Asks[0].Price;
|
||||
var tw_buy1=tw_depth.Bids[0].Price;
|
||||
var tw_records=_C(exchange.GetRecords,PERIOD_H1);
|
||||
if (tw_records.length<=50){
|
||||
Log("tw_records.length无效,跳过此次执行...");
|
||||
Sleep(wait_ms);
|
||||
continue;
|
||||
}
|
||||
|
||||
exchange.SetContractType("next_week");
|
||||
var nw_depth=_C(exchange.GetDepth);
|
||||
var nw_sell1=nw_depth.Asks[0].Price;
|
||||
var nw_buy1=nw_depth.Bids[0].Price;
|
||||
var nw_records=_C(exchange.GetRecords,PERIOD_H1);
|
||||
if (nw_records.length<=50){
|
||||
Log("nw_records.length无效,跳过此次执行...");
|
||||
Sleep(wait_ms);
|
||||
continue;
|
||||
}
|
||||
|
||||
exchange.SetContractType("quarter");
|
||||
var qt_depth=_C(exchange.GetDepth);
|
||||
var qt_sell1=qt_depth.Asks[0].Price;
|
||||
var qt_buy1=qt_depth.Bids[0].Price;
|
||||
var qt_records=_C(exchange.GetRecords,PERIOD_H1);
|
||||
if (qt_records.length<=50){
|
||||
Log("qt_records.length无效,跳过此次执行...");
|
||||
Sleep(wait_ms);
|
||||
continue;
|
||||
}
|
||||
|
||||
var tw_price_ma = TA.MA(tw_records, 30).slice(-1)[0];
|
||||
var nw_price_ma = TA.MA(nw_records, 30).slice(-1)[0];
|
||||
var qt_price_ma = TA.MA(qt_records, 30).slice(-1)[0];
|
||||
|
||||
var position=_C(exchange.GetPosition);
|
||||
|
||||
var tw_zuoduo_zhangshu=0;
|
||||
var tw_zuoduo_avg_price=0;
|
||||
var tw_zuoduo_amount=0;
|
||||
var tw_zuokong_zhangshu=0;
|
||||
var tw_zuokong_avg_price=0;
|
||||
var tw_zuokong_amount=0;
|
||||
|
||||
var nw_zuoduo_zhangshu=0;
|
||||
var nw_zuoduo_avg_price=0;
|
||||
var nw_zuoduo_amount=0;
|
||||
var nw_zuokong_zhangshu=0;
|
||||
var nw_zuokong_avg_price=0;
|
||||
var nw_zuokong_amount=0;
|
||||
|
||||
var qt_zuoduo_zhangshu=0;
|
||||
var qt_zuoduo_avg_price=0;
|
||||
var qt_zuoduo_amount=0;
|
||||
var qt_zuokong_zhangshu=0;
|
||||
var qt_zuokong_avg_price=0;
|
||||
var qt_zuokong_amount=0;
|
||||
|
||||
|
||||
for (var i=0; i < position.length; i++){
|
||||
if (position[i].ContractType==="this_week"){
|
||||
if (position[i].Type===PD_LONG){
|
||||
tw_zuoduo_zhangshu=position[i].Amount;
|
||||
tw_zuoduo_avg_price=position[i].Price;
|
||||
tw_zuoduo_amount=tw_zuoduo_zhangshu*contract_min[$.get_exchange_id(exchange)]*(1/tw_zuoduo_avg_price-1/tw_buy1+1/tw_zuoduo_avg_price);
|
||||
}
|
||||
if (position[i].Type===PD_SHORT){
|
||||
tw_zuokong_zhangshu=position[i].Amount;
|
||||
tw_zuokong_avg_price=position[i].Price;
|
||||
tw_zuokong_amount=tw_zuokong_zhangshu*contract_min[$.get_exchange_id(exchange)]*(1/tw_sell1-1/tw_zuokong_avg_price+1/tw_zuokong_avg_price);
|
||||
}
|
||||
}
|
||||
if (position[i].ContractType==="next_week"){
|
||||
if (position[i].Type===PD_LONG){
|
||||
nw_zuoduo_zhangshu=position[i].Amount;
|
||||
nw_zuoduo_avg_price=position[i].Price;
|
||||
nw_zuoduo_amount=nw_zuoduo_zhangshu*contract_min[$.get_exchange_id(exchange)]*(1/nw_zuoduo_avg_price-1/nw_buy1+1/nw_zuoduo_avg_price);
|
||||
}
|
||||
if (position[i].Type===PD_SHORT){
|
||||
nw_zuokong_zhangshu=position[i].Amount;
|
||||
nw_zuokong_avg_price=position[i].Price;
|
||||
nw_zuokong_amount=nw_zuokong_zhangshu*contract_min[$.get_exchange_id(exchange)]*(1/nw_sell1-1/nw_zuokong_avg_price+1/nw_zuokong_avg_price);
|
||||
}
|
||||
}
|
||||
if (position[i].ContractType==="quarter"){
|
||||
if (position[i].Type===PD_LONG){
|
||||
qt_zuoduo_zhangshu=position[i].Amount;
|
||||
qt_zuoduo_avg_price=position[i].Price;
|
||||
qt_zuoduo_amount=qt_zuoduo_zhangshu*contract_min[$.get_exchange_id(exchange)]*(1/qt_zuoduo_avg_price-1/qt_buy1+1/qt_zuoduo_avg_price);
|
||||
}
|
||||
if (position[i].Type===PD_SHORT){
|
||||
qt_zuokong_zhangshu=position[i].Amount;
|
||||
qt_zuokong_avg_price=position[i].Price;
|
||||
qt_zuokong_amount=qt_zuokong_zhangshu*contract_min[$.get_exchange_id(exchange)]*(1/qt_sell1-1/qt_zuokong_avg_price+1/qt_zuokong_avg_price);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var account_rights=account.Info.info[ok_future_target].account_rights;//账户权益
|
||||
var keep_deposit=account.Info.info[ok_future_target].keep_deposit;//保证金
|
||||
var profit_real=account.Info.info[ok_future_target].profit_real;//已实现盈亏
|
||||
var profit_unreal=account.Info.info[ok_future_target].profit_unreal;//未实现盈亏
|
||||
var risk_rate=account.Info.info[ok_future_target].risk_rate;//保证金率 10倍杠杆,当保证金率小于等于10%,才会触发爆仓线;20倍杠杆,当保证金率小于等于20%,才会触发爆仓线。 这意味着如果您开10倍LTC合约,当您的亏损达到开仓保证金的90%时,会触发爆仓线;若开20倍杠杆的合约,当您的亏损达到开仓保证金的80%时,才会触发爆仓线。
|
||||
|
||||
|
||||
var tw_buy1_fixed=tw_buy1;
|
||||
var tw_sell1_fixed=tw_sell1;
|
||||
var nw_buy1_fixed=nw_buy1-(nw_price_ma-tw_price_ma);
|
||||
var nw_sell1_fixed=nw_sell1-(nw_price_ma-tw_price_ma);
|
||||
var qt_buy1_fixed=qt_buy1-(qt_price_ma-tw_price_ma);
|
||||
var qt_sell1_fixed=qt_sell1-(qt_price_ma-tw_price_ma);
|
||||
|
||||
//this week - next week - kaichang
|
||||
if (tw_sell1_fixed<nw_buy1_fixed && (nw_buy1_fixed-tw_sell1_fixed)/tw_sell1_fixed>jiacha_monitor['tw_nw']){
|
||||
if (push_notification){
|
||||
Log("次周_当周_套利机会:"+exname+" "+((nw_buy1_fixed-tw_sell1_fixed)/tw_sell1_fixed)+"@");
|
||||
}
|
||||
if (risk_rate>keep_risk_rate && account.Stocks>0){
|
||||
exchange.SetContractType("this_week");
|
||||
exchange.SetDirection("buy");
|
||||
var dealamount=$.perform_limited_order("buy",exchange,tw_sell1,trade_unit,false,"futures","buy");
|
||||
if (dealamount>0){
|
||||
exchange.SetContractType("next_week");
|
||||
exchange.SetDirection("sell");
|
||||
$.perform_limited_order("buy",exchange,nw_buy1,dealamount,true,"futures","sell");
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (nw_sell1_fixed<tw_buy1_fixed && (tw_buy1_fixed-nw_sell1_fixed)/nw_sell1_fixed>jiacha_monitor['tw_nw']){
|
||||
if (push_notification){
|
||||
Log("当周_次周_套利机会:"+exname+" "+((tw_buy1_fixed-nw_sell1_fixed)/nw_sell1_fixed)+"@");
|
||||
}
|
||||
if (risk_rate>keep_risk_rate && account.Stocks>0){
|
||||
exchange.SetContractType("next_week");
|
||||
exchange.SetDirection("buy");
|
||||
var dealamount=$.perform_limited_order("buy",exchange,nw_sell1,trade_unit,false,"futures","buy");
|
||||
if (dealamount>0){
|
||||
exchange.SetContractType("this_week");
|
||||
exchange.SetDirection("sell");
|
||||
$.perform_limited_order("buy",exchange,tw_buy1,dealamount,true,"futures","sell");
|
||||
}
|
||||
}
|
||||
}
|
||||
//this week - quarter - kaichang
|
||||
else if (tw_sell1_fixed<qt_buy1_fixed && (qt_buy1_fixed-tw_sell1_fixed)/tw_sell1_fixed>jiacha_monitor['tw_qt']){
|
||||
if (push_notification){
|
||||
Log("季度_当周_套利机会:"+exname+" "+((qt_buy1_fixed-tw_sell1_fixed)/tw_sell1_fixed)+"@");
|
||||
}
|
||||
if (risk_rate>keep_risk_rate && account.Stocks>0){
|
||||
exchange.SetContractType("this_week");
|
||||
exchange.SetDirection("buy");
|
||||
var dealamount=$.perform_limited_order("buy",exchange,tw_sell1,trade_unit,false,"futures","buy");
|
||||
if (dealamount>0){
|
||||
exchange.SetContractType("quarter");
|
||||
exchange.SetDirection("sell");
|
||||
$.perform_limited_order("buy",exchange,qt_buy1,dealamount,true,"futures","sell");
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (qt_sell1_fixed<tw_buy1_fixed && (tw_buy1_fixed-qt_sell1_fixed)/qt_sell1_fixed>jiacha_monitor['tw_qt']){
|
||||
if (push_notification){
|
||||
Log("当周_季度_套利机会:"+exname+" "+((tw_buy1_fixed-qt_sell1_fixed)/qt_sell1_fixed)+"@");
|
||||
}
|
||||
if (risk_rate>keep_risk_rate && account.Stocks>0){
|
||||
exchange.SetContractType("quarter");
|
||||
exchange.SetDirection("buy");
|
||||
var dealamount=$.perform_limited_order("buy",exchange,qt_sell1,trade_unit,false,"futures","buy");
|
||||
if (dealamount>0){
|
||||
exchange.SetContractType("this_week");
|
||||
exchange.SetDirection("sell");
|
||||
$.perform_limited_order("buy",exchange,tw_buy1,dealamount,true,"futures","sell");
|
||||
}
|
||||
}
|
||||
}
|
||||
//next week - quarter - kaichang
|
||||
else if (nw_sell1_fixed<qt_buy1_fixed && (qt_buy1_fixed-nw_sell1_fixed)/nw_sell1_fixed>jiacha_monitor['nw_qt']){
|
||||
if (push_notification){
|
||||
Log("季度_次周_套利机会:"+exname+" "+((qt_buy1_fixed-nw_sell1_fixed)/nw_sell1_fixed)+"@");
|
||||
}
|
||||
if (risk_rate>keep_risk_rate && account.Stocks>0){
|
||||
exchange.SetContractType("next_week");
|
||||
exchange.SetDirection("buy");
|
||||
var dealamount=$.perform_limited_order("buy",exchange,nw_sell1,trade_unit,false,"futures","buy");
|
||||
if (dealamount>0){
|
||||
exchange.SetContractType("quarter");
|
||||
exchange.SetDirection("sell");
|
||||
$.perform_limited_order("buy",exchange,qt_buy1,dealamount,true,"futures","sell");
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (qt_sell1_fixed<nw_buy1_fixed && (nw_buy1_fixed-qt_sell1_fixed)/qt_sell1_fixed>jiacha_monitor['nw_qt']){
|
||||
if (push_notification){
|
||||
Log("次周_季度_套利机会:"+exname+" "+((nw_buy1_fixed-qt_sell1_fixed)/qt_sell1_fixed)+"@");
|
||||
}
|
||||
if (risk_rate>keep_risk_rate && account.Stocks>0){
|
||||
exchange.SetContractType("quarter");
|
||||
exchange.SetDirection("buy");
|
||||
var dealamount=$.perform_limited_order("buy",exchange,qt_sell1,trade_unit,false,"futures","buy");
|
||||
if (dealamount>0){
|
||||
exchange.SetContractType("next_week");
|
||||
exchange.SetDirection("sell");
|
||||
$.perform_limited_order("buy",exchange,nw_buy1,dealamount,true,"futures","sell");
|
||||
}
|
||||
}
|
||||
}
|
||||
//this week - next week - pingchang
|
||||
else if (Math.abs((nw_sell1_fixed-tw_buy1_fixed)/tw_buy1_fixed)<hulie_monitor['tw_nw'] && tw_zuoduo_zhangshu>0 && nw_zuokong_zhangshu>0){
|
||||
var pingchang_zhangshu=Math.min(tw_zuoduo_zhangshu,nw_zuokong_zhangshu);
|
||||
exchange.SetContractType("this_week");
|
||||
exchange.SetDirection("closebuy");
|
||||
var dealamount=$.perform_limited_order("sell",exchange,tw_buy1,pingchang_zhangshu,false,"futures","closebuy");
|
||||
if (dealamount>0){
|
||||
exchange.SetContractType("next_week");
|
||||
exchange.SetDirection("closesell");
|
||||
$.perform_limited_order("sell",exchange,nw_sell1,dealamount,true,"futures","closesell");
|
||||
}
|
||||
}
|
||||
else if (Math.abs((tw_sell1_fixed-nw_buy1_fixed)/nw_buy1_fixed)<hulie_monitor['tw_nw'] && tw_zuokong_zhangshu>0 && nw_zuoduo_zhangshu>0){
|
||||
var pingchang_zhangshu=Math.min(tw_zuokong_zhangshu,nw_zuoduo_zhangshu);
|
||||
exchange.SetContractType("next_week");
|
||||
exchange.SetDirection("closebuy");
|
||||
var dealamount=$.perform_limited_order("sell",exchange,nw_buy1,pingchang_zhangshu,false,"futures","closebuy");
|
||||
if (dealamount>0){
|
||||
exchange.SetContractType("this_week");
|
||||
exchange.SetDirection("closesell");
|
||||
$.perform_limited_order("sell",exchange,tw_sell1,dealamount,true,"futures","closesell");
|
||||
}
|
||||
}
|
||||
//this week - quarter - pingchang
|
||||
else if (Math.abs((qt_sell1_fixed-tw_buy1_fixed)/tw_buy1_fixed)<hulie_monitor['tw_qt'] && tw_zuoduo_zhangshu>0 && qt_zuokong_zhangshu>0){
|
||||
var pingchang_zhangshu=Math.min(tw_zuoduo_zhangshu,qt_zuokong_zhangshu);
|
||||
exchange.SetContractType("this_week");
|
||||
exchange.SetDirection("closebuy");
|
||||
var dealamount=$.perform_limited_order("sell",exchange,tw_buy1,pingchang_zhangshu,false,"futures","closebuy");
|
||||
if (dealamount>0){
|
||||
exchange.SetContractType("quarter");
|
||||
exchange.SetDirection("closesell");
|
||||
$.perform_limited_order("sell",exchange,qt_sell1,dealamount,true,"futures","closesell");
|
||||
}
|
||||
}
|
||||
else if (Math.abs((tw_sell1_fixed-qt_buy1_fixed)/qt_buy1_fixed)<hulie_monitor['tw_qt'] && tw_zuokong_zhangshu>0 && qt_zuoduo_zhangshu>0){
|
||||
var pingchang_zhangshu=Math.min(tw_zuokong_zhangshu,qt_zuoduo_zhangshu);
|
||||
exchange.SetContractType("quarter");
|
||||
exchange.SetDirection("closebuy");
|
||||
var dealamount=$.perform_limited_order("sell",exchange,qt_buy1,pingchang_zhangshu,false,"futures","closebuy");
|
||||
if (dealamount>0){
|
||||
exchange.SetContractType("this_week");
|
||||
exchange.SetDirection("closesell");
|
||||
$.perform_limited_order("sell",exchange,tw_sell1,dealamount,true,"futures","closesell");
|
||||
}
|
||||
}
|
||||
//next week - quarter - pingchang
|
||||
else if (Math.abs((qt_sell1_fixed-nw_buy1_fixed)/nw_buy1_fixed)<hulie_monitor['nw_qt'] && nw_zuoduo_zhangshu>0 && qt_zuokong_zhangshu>0){
|
||||
var pingchang_zhangshu=Math.min(nw_zuoduo_zhangshu,qt_zuokong_zhangshu);
|
||||
exchange.SetContractType("next_week");
|
||||
exchange.SetDirection("closebuy");
|
||||
var dealamount=$.perform_limited_order("sell",exchange,nw_buy1,pingchang_zhangshu,false,"futures","closebuy");
|
||||
if (dealamount>0){
|
||||
exchange.SetContractType("quarter");
|
||||
exchange.SetDirection("closesell");
|
||||
$.perform_limited_order("sell",exchange,qt_sell1,dealamount,true,"futures","closesell");
|
||||
}
|
||||
}
|
||||
else if (Math.abs((nw_sell1_fixed-qt_buy1_fixed)/qt_buy1_fixed)<hulie_monitor['nw_qt'] && nw_zuokong_zhangshu>0 && qt_zuoduo_zhangshu>0){
|
||||
var pingchang_zhangshu=Math.min(nw_zuokong_zhangshu,qt_zuoduo_zhangshu);
|
||||
exchange.SetContractType("quarter");
|
||||
exchange.SetDirection("closebuy");
|
||||
var dealamount=$.perform_limited_order("sell",exchange,qt_buy1,pingchang_zhangshu,false,"futures","closebuy");
|
||||
if (dealamount>0){
|
||||
exchange.SetContractType("next_week");
|
||||
exchange.SetDirection("closesell");
|
||||
$.perform_limited_order("sell",exchange,nw_sell1,dealamount,true,"futures","closesell");
|
||||
}
|
||||
}
|
||||
else{
|
||||
//处理交割
|
||||
var total_zuoduo=tw_zuoduo_zhangshu+nw_zuoduo_zhangshu+qt_zuoduo_zhangshu;
|
||||
var total_zuokong=tw_zuokong_zhangshu+nw_zuokong_zhangshu+qt_zuokong_zhangshu;
|
||||
if (total_zuoduo!==total_zuokong){
|
||||
if (total_zuoduo>total_zuokong){
|
||||
var diff_num=total_zuoduo-total_zuokong;
|
||||
|
||||
//强制平多
|
||||
Log("开始强制平多:"+diff_num+'@');
|
||||
if (qt_zuoduo_zhangshu>=diff_num){
|
||||
exchange.SetContractType("quarter");
|
||||
exchange.SetDirection("closebuy");
|
||||
$.perform_limited_order("sell",exchange,qt_buy1,diff_num,true,"futures","closebuy");
|
||||
}else{
|
||||
exchange.SetContractType("quarter");
|
||||
exchange.SetDirection("closebuy");
|
||||
$.perform_limited_order("sell",exchange,qt_buy1,qt_zuoduo_zhangshu,true,"futures","closebuy");
|
||||
var diff2=diff_num-qt_zuoduo_zhangshu;
|
||||
if (nw_zuoduo_zhangshu>=diff2){
|
||||
exchange.SetContractType("next_week");
|
||||
exchange.SetDirection("closebuy");
|
||||
$.perform_limited_order("sell",exchange,nw_buy1,diff2,true,"futures","closebuy");
|
||||
}else{
|
||||
exchange.SetContractType("next_week");
|
||||
exchange.SetDirection("closebuy");
|
||||
$.perform_limited_order("sell",exchange,nw_buy1,nw_zuoduo_zhangshu,true,"futures","closebuy");
|
||||
var diff3=diff2-nw_zuoduo_zhangshu;
|
||||
if (tw_zuoduo_zhangshu>=diff3){
|
||||
exchange.SetContractType("this_week");
|
||||
exchange.SetDirection("closebuy");
|
||||
$.perform_limited_order("sell",exchange,tw_buy1,diff3,true,"futures","closebuy");
|
||||
}else{
|
||||
exchange.SetContractType("this_week");
|
||||
exchange.SetDirection("closebuy");
|
||||
$.perform_limited_order("sell",exchange,tw_buy1,tw_zuoduo_zhangshu,true,"futures","closebuy");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (total_zuokong>total_zuoduo){
|
||||
var diff_num=total_zuokong-total_zuoduo;
|
||||
|
||||
//强制平空
|
||||
Log("开始强制平空:"+diff_num+'@');
|
||||
if (qt_zuokong_zhangshu>=diff_num){
|
||||
exchange.SetContractType("quarter");
|
||||
exchange.SetDirection("closesell");
|
||||
$.perform_limited_order("sell",exchange,qt_sell1,diff_num,true,"futures","closesell");
|
||||
}else{
|
||||
exchange.SetContractType("quarter");
|
||||
exchange.SetDirection("closesell");
|
||||
$.perform_limited_order("sell",exchange,qt_sell1,qt_zuokong_zhangshu,true,"futures","closesell");
|
||||
var diff2=diff_num-qt_zuokong_zhangshu;
|
||||
if (nw_zuokong_zhangshu>=diff2){
|
||||
exchange.SetContractType("next_week");
|
||||
exchange.SetDirection("closesell");
|
||||
$.perform_limited_order("sell",exchange,nw_sell1,diff2,true,"futures","closesell");
|
||||
}else{
|
||||
exchange.SetContractType("next_week");
|
||||
exchange.SetDirection("closesell");
|
||||
$.perform_limited_order("sell",exchange,nw_sell1,nw_zuokong_zhangshu,true,"futures","closesell");
|
||||
var diff3=diff2-nw_zuokong_zhangshu;
|
||||
if (tw_zuokong_zhangshu>=diff3){
|
||||
exchange.SetContractType("this_week");
|
||||
exchange.SetDirection("closesell");
|
||||
$.perform_limited_order("sell",exchange,tw_sell1,diff3,true,"futures","closesell");
|
||||
}else{
|
||||
exchange.SetContractType("this_week");
|
||||
exchange.SetDirection("closesell");
|
||||
$.perform_limited_order("sell",exchange,tw_sell1,tw_zuokong_zhangshu,true,"futures","closesell");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
LogStatus(
|
||||
"合约近期均价="+tw_price_ma+'/'+nw_price_ma+'/'+qt_price_ma+"\n"+
|
||||
'-----------------------------------------------------------\n'+
|
||||
'次周_当周_开仓差价='+(nw_buy1_fixed-tw_sell1_fixed)/tw_sell1_fixed+'/'+jiacha_monitor['tw_nw']+"\n"+
|
||||
'当周_次周_开仓差价='+(tw_buy1_fixed-nw_sell1_fixed)/nw_sell1_fixed+'/'+jiacha_monitor['tw_nw']+"\n"+
|
||||
'季度_当周_开仓差价='+(qt_buy1_fixed-tw_sell1_fixed)/tw_sell1_fixed+'/'+jiacha_monitor['tw_qt']+"\n"+
|
||||
'当周_季度_开仓差价='+(tw_buy1_fixed-qt_sell1_fixed)/qt_sell1_fixed+'/'+jiacha_monitor['tw_qt']+"\n"+
|
||||
'季度_次周_开仓差价='+(qt_buy1_fixed-nw_sell1_fixed)/nw_sell1_fixed+'/'+jiacha_monitor['nw_qt']+"\n"+
|
||||
'次周_季度_开仓差价='+(nw_buy1_fixed-qt_sell1_fixed)/qt_sell1_fixed+'/'+jiacha_monitor['nw_qt']+"\n"+
|
||||
'-----------------------------------------------------------\n'+
|
||||
'次周_当周_平仓差价='+Math.abs((nw_sell1_fixed-tw_buy1_fixed)/tw_buy1_fixed)+'/'+hulie_monitor['tw_nw']+"\n"+
|
||||
'当周_次周_平仓差价='+Math.abs((tw_sell1_fixed-nw_buy1_fixed)/nw_buy1_fixed)+'/'+hulie_monitor['tw_nw']+"\n"+
|
||||
'季度_当周_平仓差价='+Math.abs((qt_sell1_fixed-tw_buy1_fixed)/tw_buy1_fixed)+'/'+hulie_monitor['tw_qt']+"\n"+
|
||||
'当周_季度_平仓差价='+Math.abs((tw_sell1_fixed-qt_buy1_fixed)/qt_buy1_fixed)+'/'+hulie_monitor['tw_qt']+"\n"+
|
||||
'季度_次周_平仓差价='+Math.abs((qt_sell1_fixed-nw_buy1_fixed)/nw_buy1_fixed)+'/'+hulie_monitor['nw_qt']+"\n"+
|
||||
'次周_季度_平仓差价='+Math.abs((nw_sell1_fixed-qt_buy1_fixed)/qt_buy1_fixed)+'/'+hulie_monitor['nw_qt']+"\n"+
|
||||
'-----------------------------------------------------------\n'+
|
||||
'账户权益='+account_rights+'\n'+
|
||||
'已用保证金='+keep_deposit+'\n'+
|
||||
'可用保证金='+account.Stocks+'\n'+
|
||||
'保证金率='+risk_rate+'\n'+
|
||||
'当周做多/做空张数='+tw_zuoduo_zhangshu+'/'+tw_zuokong_zhangshu+'\n'+
|
||||
'次周做多/做空张数='+nw_zuoduo_zhangshu+'/'+nw_zuokong_zhangshu+'\n'+
|
||||
'季度做多/做空张数='+qt_zuoduo_zhangshu+'/'+qt_zuokong_zhangshu+'\n'+
|
||||
'期货仓位='+position.length+'\n'+
|
||||
'未完成订单='+f_orders.length+'\n'+
|
||||
'当周做多/做空均价='+tw_zuoduo_avg_price+'/'+tw_zuokong_avg_price+'\n'+
|
||||
'次周做多/做空均价='+nw_zuoduo_avg_price+'/'+nw_zuokong_avg_price+'\n'+
|
||||
'季度做多/做空均价='+qt_zuoduo_avg_price+'/'+qt_zuokong_avg_price+'\n'+
|
||||
'♜轮询次数: '+total_loop+'\n'+
|
||||
'♜更新时间: '+$.get_ChinaTimeString()+'\n'+
|
||||
'♜微信: alinwo(验证消息:botvs) #0000ff'+'\n'+
|
||||
'♜霖霖量化-实盘策略: http://www.pcclean.io/quant #ff0000'+'\n'
|
||||
);
|
||||
|
||||
if (total_loop%200===0){
|
||||
LogProfit(account_rights);
|
||||
}
|
||||
|
||||
total_loop++;
|
||||
Sleep(wait_ms);
|
||||
|
||||
}//while end
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
> 策略出处
|
||||
|
||||
https://www.fmz.com/strategy/157635
|
||||
|
||||
> 更新时间
|
||||
|
||||
2019-07-17 22:39:05
|
||||
+392
@@ -0,0 +1,392 @@
|
||||
|
||||
> 策略名称
|
||||
|
||||
RSI统计套利策略
|
||||
|
||||
> 策略作者
|
||||
|
||||
quant777
|
||||
|
||||
> 策略描述
|
||||
|
||||
此策略是基于RSI指标的统计套利策略,根据实测在熊市中也能有很高的胜率。策略会对行情进行RSI数据分析,一旦捕获到预定义的K线形态即进行短期套利。
|
||||
|
||||
RSI指标说明:
|
||||
RSI最早被用于期货交易中,后来人们发现用该指标来指导股票市场投资效果也十分不错,并对该指标的特点不断进行归纳和总结。现在,RSI已经成为被投资者应用最广泛的技术指标之一。投资的一般原理认为,投资者的买卖行为是各种因素综合结果的反映,行情的变化最终取决于供求关系,而RSI指标正是根据供求平衡的原理,通过测量某一个期间内股价上涨总幅度占股价变化总幅度平均值的百分比,来评估多空力量的强弱程度,进而提示具体操作的。
|
||||
|
||||
策略特点:
|
||||
支持任意级别的趋势跟踪(分钟K,小时K,日K,周K等)
|
||||
支持任意交易对(ETH/BTC, BSV/BTC等)
|
||||
支持任意交易所
|
||||
详细的策略报表(包括策略状态,交易历史记录等)
|
||||
支持接近10个自定义个性化参数
|
||||
|
||||
策略参数说明:
|
||||
https://www.pcclean.io/8cut
|
||||
|
||||
|
||||
|
||||
> 源码 (javascript)
|
||||
|
||||
``` javascript
|
||||
/*
|
||||
RSI策略:仅用于学习用途,用于实盘后果自负
|
||||
*/
|
||||
|
||||
var ExchangProcessor={
|
||||
createNew: function(exc_obj){
|
||||
//策略参数
|
||||
var manage_assets=1;//bch
|
||||
var max_positions=4;//max=4N
|
||||
var price_n={BTC_BCH:8,ETH_BCH:8,XRP_BCH:8,EOS_BCH:8,LTC_BCH:8,DASH_BCH:8,CET_BCH:8}; //价格精度
|
||||
var num_n={BTC_BCH:8,ETH_BCH:8,XRP_BCH:8,EOS_BCH:8,LTC_BCH:8,DASH_BCH:8,CET_BCH:8}; //数量精度
|
||||
var minest_buy={BTC_BCH:0.001,ETH_BCH:0.01,XRP_BCH:1,EOS_BCH:1,LTC_BCH:0.1,DASH_BCH:0.01,CET_BCH:1};//最小买入量
|
||||
var minest_sell={BTC_BCH:0.001,ETH_BCH:0.01,XRP_BCH:1,EOS_BCH:1,LTC_BCH:0.1,DASH_BCH:0.01,CET_BCH:1};//最小卖出量
|
||||
var order_wait_secs=120000; //订单的最长等待时间 毫秒
|
||||
var wait_ms=1000;//默认等待毫秒
|
||||
var sxf=0.0005;//用来计算手续费消耗
|
||||
|
||||
|
||||
//全局状态变量
|
||||
var positions=[];//记录仓位
|
||||
var init_asset=0; //初始资产
|
||||
var trades=[];//所有交易
|
||||
var trades_recorder=true;//记录所有交易
|
||||
var pre_time=null; //记录轮询间隔时间
|
||||
var approximate_profit=0;//盈亏近似值
|
||||
var add_already=0;//已经加仓次数
|
||||
|
||||
var processor={};
|
||||
//重试购买,直到成功返回
|
||||
processor.retryBuy=function(ex,price,num)
|
||||
{
|
||||
var currency=ex.GetCurrency();
|
||||
var r=ex.Buy(_N(price,price_n[currency]), _N(num,num_n[currency]));
|
||||
while (!r){
|
||||
Log("Buy失败,正在retry。");
|
||||
Sleep(wait_ms);
|
||||
var account=_C(ex.GetAccount);
|
||||
var ticker=_C(ex.GetTicker);
|
||||
var last=ticker.Last;
|
||||
var fixedAmount=(price===-1?Math.min(account.Balance*0.95,num):Math.min(account.Balance/last*0.95,num));
|
||||
r=ex.Buy(_N(price,price_n[currency]), _N(fixedAmount,num_n[currency]));
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
//重试卖出,直到成功返回
|
||||
processor.retrySell=function(ex,price,num){
|
||||
var currency=ex.GetCurrency();
|
||||
var r=ex.Sell(_N(price,price_n[currency]), _N(num,num_n[currency]));
|
||||
while (!r){
|
||||
Log("Sell失败,正在retry。");
|
||||
Sleep(wait_ms);
|
||||
var account=_C(ex.GetAccount);
|
||||
var fixedAmount=Math.min(account.Stocks,num);
|
||||
r=ex.Sell(_N(price,price_n[currency]), _N(fixedAmount,num_n[currency]));
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
processor.get_ChinaTimeString=function(){
|
||||
var date = new Date();
|
||||
var now_utc = Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate(),
|
||||
date.getUTCHours(), date.getUTCMinutes(), date.getUTCSeconds());
|
||||
var cdate=new Date(now_utc);
|
||||
cdate.setHours(cdate.getHours()+8);
|
||||
var localstring=cdate.getFullYear()+'/'+(cdate.getMonth()+1)+'/'+cdate.getDate()+' '+cdate.getHours()+':'+cdate.getMinutes()+':'+cdate.getSeconds();
|
||||
return localstring;
|
||||
}
|
||||
|
||||
processor.init_obj=function(){
|
||||
_CDelay(wait_ms);
|
||||
pre_time = new Date();
|
||||
|
||||
//init
|
||||
{
|
||||
var account=_C(exc_obj.GetAccount);
|
||||
var ticker=_C(exc_obj.GetTicker);
|
||||
var last=ticker.Last;
|
||||
init_asset=(account.Balance+account.FrozenBalance)+(account.Stocks+account.FrozenStocks)*last;
|
||||
Sleep(wait_ms);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
processor.work=function(){
|
||||
var cur_time = new Date();
|
||||
var passedtime=cur_time-pre_time;
|
||||
pre_time=cur_time;
|
||||
|
||||
//计算n,头寸
|
||||
var exname=exc_obj.GetName();
|
||||
var currency=exc_obj.GetCurrency();
|
||||
var account=_C(exc_obj.GetAccount);
|
||||
var ticker=_C(exc_obj.GetTicker);
|
||||
var depth = _C(exc_obj.GetDepth);
|
||||
var last=ticker.Last;
|
||||
var ask1=depth.Asks[0].Price;
|
||||
var bid1=depth.Bids[0].Price;
|
||||
var bestprice=bid1+(Math.abs(ask1-bid1)/2);
|
||||
var records = _C(exc_obj.GetRecords);
|
||||
if (records.length<=50){
|
||||
Log("records.length is not valid.");
|
||||
Sleep(wait_ms);
|
||||
return;
|
||||
}
|
||||
var atr = TA.ATR(records, 20);
|
||||
if (atr.length<=1){
|
||||
Log("atr.length is not valid.");
|
||||
Sleep(wait_ms);
|
||||
return;
|
||||
}
|
||||
var N=atr[atr.length-1];
|
||||
var position_unit=Math.min(manage_assets*0.01/N,account.Balance/last*0.95);//cet
|
||||
//Log("N="+N+", 头寸单位="+position_unit+"CET");
|
||||
var highest=TA.Highest(records, 20, 'High');
|
||||
var Lowest=TA.Lowest(records, 10, 'Low');
|
||||
var cur_asset=(account.Balance+account.FrozenBalance)+(account.Stocks+account.FrozenStocks)*last;
|
||||
var rsi6 = TA.RSI(records, 6);
|
||||
var rsi12 = TA.RSI(records, 12);
|
||||
if (rsi6.length<=5 || rsi12.length<=5){
|
||||
Log("rsi is not valid.");
|
||||
Sleep(wait_ms);
|
||||
return;
|
||||
}
|
||||
var rsi_in=false;
|
||||
if (rsi6[rsi6.length-1]-rsi6[rsi6.length-2]>1 && rsi6[rsi6.length-2]<=65){
|
||||
//Log("rsi_in=true");
|
||||
rsi_in=true;
|
||||
}
|
||||
var rsi_out=false;
|
||||
if (rsi6[rsi6.length-1]>=60){
|
||||
//Log("rsi_out=true");
|
||||
rsi_out=true;
|
||||
}
|
||||
|
||||
//建仓
|
||||
if (positions.length==0 && position_unit>minest_buy[currency]){
|
||||
if (rsi_in)
|
||||
{
|
||||
var buyID = processor.retryBuy(exc_obj,last,position_unit);
|
||||
Sleep(order_wait_secs);
|
||||
var buyOrder=_C(exc_obj.GetOrder,buyID);
|
||||
if (buyOrder.Status!=ORDER_STATE_CLOSED){
|
||||
exc_obj.CancelOrder(buyID);
|
||||
}
|
||||
if (buyOrder.DealAmount>0){
|
||||
var postion = {
|
||||
amount:buyOrder.DealAmount,
|
||||
buy_price:buyOrder.AvgPrice,
|
||||
stoploss_price:buyOrder.AvgPrice-N};
|
||||
positions.push(postion);
|
||||
|
||||
var details={
|
||||
type:"建仓",
|
||||
time:processor.get_ChinaTimeString(),
|
||||
RealAmount:buyOrder.DealAmount,
|
||||
WantAmount:position_unit,
|
||||
RealPrice:buyOrder.AvgPrice,
|
||||
WantPrice:buyOrder.Price,
|
||||
Memo:""
|
||||
};
|
||||
if (trades_recorder){
|
||||
trades.push(details);
|
||||
}
|
||||
|
||||
add_already=1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//加仓
|
||||
if (positions.length>0 && position_unit>minest_buy[currency]){
|
||||
var last_buy_price=positions[positions.length-1].buy_price;
|
||||
if (add_already<max_positions){
|
||||
if (last-last_buy_price>=0.5*N){
|
||||
var buyID = processor.retryBuy(exc_obj,last,position_unit);
|
||||
Sleep(order_wait_secs);
|
||||
var buyOrder=_C(exc_obj.GetOrder,buyID);
|
||||
if (buyOrder.Status!=ORDER_STATE_CLOSED){
|
||||
exc_obj.CancelOrder(buyID);
|
||||
}
|
||||
if (buyOrder.DealAmount>0){
|
||||
var postion = {
|
||||
amount:buyOrder.DealAmount,
|
||||
buy_price:buyOrder.AvgPrice,
|
||||
stoploss_price:buyOrder.AvgPrice-N};
|
||||
positions.push(postion);
|
||||
|
||||
var details={
|
||||
type:"加仓",
|
||||
time:processor.get_ChinaTimeString(),
|
||||
RealAmount:buyOrder.DealAmount,
|
||||
WantAmount:position_unit,
|
||||
RealPrice:buyOrder.AvgPrice,
|
||||
WantPrice:last,
|
||||
Memo:""
|
||||
};
|
||||
if (trades_recorder){
|
||||
trades.push(details);
|
||||
}
|
||||
|
||||
add_already=add_already+1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//止损
|
||||
if (positions.length>0){
|
||||
var positions_new=[];
|
||||
for (var i=0; i < positions.length; i++){
|
||||
if (last<=positions[i].stoploss_price){
|
||||
account=_C(exc_obj.GetAccount);
|
||||
var fixedAmount=Math.min(account.Stocks,positions[i].amount);
|
||||
if (fixedAmount>minest_sell[currency]){
|
||||
var sellID = processor.retrySell(exc_obj, last, fixedAmount);
|
||||
Sleep(order_wait_secs);
|
||||
var sellOrder=_C(exc_obj.GetOrder,sellID);
|
||||
approximate_profit+=(sellOrder.AvgPrice*sellOrder.DealAmount*(1-sxf)-positions[i].buy_price*sellOrder.DealAmount*(1+sxf));
|
||||
Log("定价卖出: 数量-"+sellOrder.DealAmount+",approximate_profit="+approximate_profit);
|
||||
if (sellOrder.Status!=ORDER_STATE_CLOSED){
|
||||
exc_obj.CancelOrder(sellID);
|
||||
if (Math.min(account.Stocks,fixedAmount-sellOrder.DealAmount)>minest_sell[currency]){
|
||||
var marketsellOrderID=processor.retrySell(exc_obj, -1, fixedAmount-sellOrder.DealAmount);
|
||||
Sleep(order_wait_secs);
|
||||
var marketsellOrderData=_C(exc_obj.GetOrder,marketsellOrderID);
|
||||
approximate_profit+=(marketsellOrderData.AvgPrice*marketsellOrderData.DealAmount*(1-sxf)-positions[i].buy_price*marketsellOrderData.DealAmount*(1+sxf));
|
||||
Log("市价卖出: 数量-"+marketsellOrderData.DealAmount+",approximate_profit="+approximate_profit);
|
||||
}
|
||||
}
|
||||
|
||||
var details={
|
||||
type:"止损",
|
||||
time:processor.get_ChinaTimeString(),
|
||||
RealAmount:-1,
|
||||
WantAmount:fixedAmount,
|
||||
RealPrice:-1,
|
||||
WantPrice:last,
|
||||
Memo:(last>positions[i].buy_price?"盈利":"亏损")
|
||||
};
|
||||
if (trades_recorder){
|
||||
trades.push(details);
|
||||
}
|
||||
}
|
||||
}else{
|
||||
positions_new.push(positions[i]);
|
||||
}
|
||||
}
|
||||
positions=positions_new;
|
||||
}
|
||||
|
||||
//清仓
|
||||
if (positions.length>0){
|
||||
if (rsi_out){
|
||||
var positions_new=[];
|
||||
for (var i=0; i < positions.length; i++){
|
||||
account=_C(exc_obj.GetAccount);
|
||||
var fixedAmount=Math.min(account.Stocks,positions[i].amount);
|
||||
if (fixedAmount>minest_sell[currency]){
|
||||
var sellID = processor.retrySell(exc_obj, last, fixedAmount);
|
||||
Sleep(order_wait_secs);
|
||||
var sellOrder=_C(exc_obj.GetOrder,sellID);
|
||||
approximate_profit+=(sellOrder.AvgPrice*sellOrder.DealAmount*(1-sxf)-positions[i].buy_price*sellOrder.DealAmount*(1+sxf));
|
||||
Log("定价卖出: 数量-"+sellOrder.DealAmount+",approximate_profit="+approximate_profit);
|
||||
if (sellOrder.Status!=ORDER_STATE_CLOSED){
|
||||
exc_obj.CancelOrder(sellID);
|
||||
if (Math.min(account.Stocks,fixedAmount-sellOrder.DealAmount)>minest_sell[currency]){
|
||||
var marketsellOrderID=processor.retrySell(exc_obj, -1, fixedAmount-sellOrder.DealAmount);
|
||||
Sleep(order_wait_secs);
|
||||
var marketsellOrderData=_C(exc_obj.GetOrder,marketsellOrderID);
|
||||
approximate_profit+=(marketsellOrderData.AvgPrice*marketsellOrderData.DealAmount*(1-sxf)-positions[i].buy_price*marketsellOrderData.DealAmount*(1+sxf));
|
||||
Log("市价卖出: 数量-"+marketsellOrderData.DealAmount+",approximate_profit="+approximate_profit);
|
||||
}
|
||||
}
|
||||
|
||||
var details={
|
||||
type:"清仓",
|
||||
time:processor.get_ChinaTimeString(),
|
||||
RealAmount:-1,
|
||||
WantAmount:fixedAmount,
|
||||
RealPrice:-1,
|
||||
WantPrice:last,
|
||||
Memo:(last>positions[i].buy_price?"盈利":"亏损")
|
||||
};
|
||||
if (trades_recorder){
|
||||
trades.push(details);
|
||||
}
|
||||
}
|
||||
}
|
||||
positions=positions_new;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//显示状态
|
||||
var table1 = {type: 'table', title: '仓位-'+exname+'('+currency+')', cols: ['数量', '成交价','止损价'], rows: []};
|
||||
var table2 = {type: 'table', title: '状态-'+exname+'('+currency+')', cols: ['平均真实波幅(N)','头寸单位','初始资产','当前资产','轮询时间','最新价','Highest','Lowest','加仓次数','【近似盈亏】'], rows: []};
|
||||
var table3 = {type: 'table', title: '交易历史-'+exname+'('+currency+')', cols: ['日期','类型', '成交数量','发单数量','成交价','发单价','备注'], rows: []};
|
||||
for (var i=0; i < positions.length; i++){
|
||||
table1.rows.push([positions[i].amount,positions[i].buy_price,positions[i].stoploss_price]);
|
||||
}
|
||||
table2.rows.push([N,position_unit,init_asset,cur_asset,passedtime+'ms',last,highest,Lowest,add_already,approximate_profit]);
|
||||
for (i=0; i < trades.length; i++){
|
||||
table3.rows.push([trades[i].time,trades[i].type,trades[i].RealAmount,trades[i].WantAmount,trades[i].RealPrice,trades[i].WantPrice,trades[i].Memo]);
|
||||
}
|
||||
processor.logstatus=('`' + JSON.stringify([table1, table2, table3])+'`'+'\n');
|
||||
|
||||
//记录盈利
|
||||
processor.logprofit=approximate_profit;
|
||||
|
||||
//rest
|
||||
Sleep(wait_ms);
|
||||
}
|
||||
|
||||
return processor;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
//主函数
|
||||
function main(){
|
||||
var exchange_num=exchanges.length;
|
||||
var processors=[];
|
||||
for (var i=0; i<exchange_num; ++i){
|
||||
var p=ExchangProcessor.createNew(exchanges[i]);
|
||||
processors.push(p);
|
||||
}
|
||||
for (i=0; i<exchange_num; ++i){
|
||||
processors[i].init_obj();
|
||||
}
|
||||
var pre_profit=Number(_G("pre_profit"));
|
||||
Log('之前收入累计:'+pre_profit);
|
||||
var lastprofit=0;
|
||||
while(true){
|
||||
var allstatus='定制策略请联系微信: alinwo (验证消息:botvs量化)#0000ff'+'\n';
|
||||
var allprofit=0;
|
||||
for (i=0; i<exchange_num; ++i){
|
||||
processors[i].work();
|
||||
allstatus+=processors[i].logstatus;
|
||||
allprofit+=processors[i].logprofit;
|
||||
}
|
||||
allstatus+=('定制策略请联系微信: alinwo (验证消息:botvs量化)#0000ff'+'\n');
|
||||
LogStatus(allstatus);
|
||||
if (lastprofit!==allprofit){
|
||||
LogProfit(pre_profit+allprofit);
|
||||
_G("pre_profit", pre_profit+allprofit);
|
||||
lastprofit=allprofit;
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
> 策略出处
|
||||
|
||||
https://www.fmz.com/strategy/157366
|
||||
|
||||
> 更新时间
|
||||
|
||||
2019-07-16 14:28:24
|
||||
@@ -0,0 +1,54 @@
|
||||
|
||||
> 策略名称
|
||||
|
||||
RangeBreak策略与波动率相结合的实战应用
|
||||
|
||||
> 策略作者
|
||||
|
||||
Hukybo
|
||||
|
||||
> 策略描述
|
||||
|
||||
**策略简介**
|
||||
RangeBreak策略最初来源于期货和外汇交易,属于日内突破策略的一种。在《Futures Truth Magazine》(美国权威交易系统评选杂志)中曾经连续多年排名前十。无论是专业的投资机构还是个人交易者都在广泛使用。但是,如果一个交易策略被大众广为所知,那么这个交易策略在实战中的应用就会大打折扣。所以,这篇文章的目的,不是介绍RangeBreak策略让大家生搬硬套,而是通过对RangeBreak策略的学习,让大家从一个盈利的交易系统中融会贯通,提高交易的能力。
|
||||
|
||||
点击:[阅读更加详细的策略介绍](https://www.fmz.com/digest-topic/4038)
|
||||
|
||||
> 策略参数
|
||||
|
||||
|
||||
|
||||
|参数|默认值|描述|
|
||||
|----|----|----|
|
||||
|N|0.5|N|
|
||||
|
||||
|
||||
> 源码 (麦语言)
|
||||
|
||||
``` pascal
|
||||
(*backtest
|
||||
start: 2015-02-22 00:00:00
|
||||
end: 2019-07-13 00:00:00
|
||||
period: 5m
|
||||
exchanges: [{"eid":"Futures_CTP","currency":"FUTURES"}]
|
||||
args: [["RunMode",1,126961],["SlideTick",0,126961],["ContractType","ZC888",126961]]
|
||||
*)
|
||||
|
||||
Q:=BARSLAST(DATE<>REF(DATE,1))+1; // 判断是不是新一天的K线
|
||||
DIFF:=REF(HHV(HIGH,Q),Q)-REF(LLV(LOW,Q),Q); // 昨日最高价与最低价的价格差
|
||||
OO:=VALUEWHEN(Q=1,OPEN); // 当天开盘价
|
||||
UP^^OO+DIFF*N1; // 上轨
|
||||
DOWN^^OO-DIFF*N2; // 下轨
|
||||
TIME>=0905&&TIME<1455&&CLOSE>UP,BK; // 多头开仓
|
||||
TIME>=0905&&TIME<1455&&CLOSE<DOWN,SK; // 空头开仓
|
||||
TIME>=1455,CLOSEOUT; // 收盘平仓
|
||||
AUTOFILTER; // 信号过滤
|
||||
```
|
||||
|
||||
> 策略出处
|
||||
|
||||
https://www.fmz.com/strategy/156836
|
||||
|
||||
> 更新时间
|
||||
|
||||
2019-07-16 16:49:03
|
||||
@@ -0,0 +1,286 @@
|
||||
|
||||
> 策略名称
|
||||
|
||||
【海龟汤策略】反趋势交易策略
|
||||
|
||||
> 策略作者
|
||||
|
||||
coin989
|
||||
|
||||
> 策略描述
|
||||
|
||||
海龟之汤,简称“龟汤”,是个与海龟交易法则相反的交易策略,它利用了跟势交易(特别是海龟方式)在很多假突破方面的缺陷来获利(把海龟做成汤吃掉)。
|
||||
|
||||
上世纪八十年代早期,有个非常著名的交易员团体——叫做“海龟”。缔造了交易传奇的市场大师理查德·丹尼斯在训练一组新交易员的时候起了这个有趣的名字。因为理查德相信,培训交易员,其实就像新加坡人养海龟一样。这个交易方法被称作海龟方法,这个简单的趋势跟随技巧方法曾令他们的导师理查德取得了巨大的成功。
|
||||
|
||||
二十多年过去了,海龟方法现在已不再是个秘密,很多人已经了解它。尽管如此,那为何仍没多少人用它交易取得成功呢?那是因为趋势跟随策略通常需要忍受大幅度和长时间的回撤。很多投资者或交易员没有足够的资本或者愿意坚持这些长期的亏损。通常大多数人在交易转向对他们有利之前就已经退出了。
|
||||
|
||||
琳达·华斯基和拉利·康诺在他们的书《华尔街精灵——屡试不爽的短期交易策略》(Street Smarts)中命名了这个与“海龟”交易法则相反的“龟汤”交易法则。大多数策略不能保持盈利的原因是,一旦很多市场参与者使用它们,这些策略的盈利能力就将退化。这是市场用来平衡那些不平衡现象的方法。华斯基通过与海龟方法相反的交易,发现更多成功和获利的结果。因为海龟法需要很多失败的交易和坐等坏的交易变得盈利。通常当它获利时,它会抵消那些所有的亏损才盈利,而且这个现象周而复始。很显然,华斯基创造这个策略“海龟之汤”的名字是种美式幽默,它利用海龟交易者的成本来获利(把海龟做成汤吃掉)。
|
||||
|
||||
原始的“海龟”交易规则至少是波段交易或更久的交易,时间跨度在一周到一个月之间。华斯基女士使用“龟汤”策略做更短期的日内或波段交易,集中地利用了假突破的高频发生现象。一般来说,“海龟”法使用的20天周期突破:突破20日高则买入,跌破20日低则抛售。而“龟汤”交易法则采用完全相反的方法。
|
||||
|
||||
策略特点:
|
||||
支持任意级别的趋势跟踪(分钟K,小时K,日K,周K等)
|
||||
支持任意交易对(ETH/BTC, BSV/BTC等)
|
||||
支持任意交易所
|
||||
详细的策略报表(包括策略状态,交易历史记录等)
|
||||
支持接近10个自定义个性化参数
|
||||
|
||||
依赖项botvs_tools模板库下载:
|
||||
https://www.pcclean.io/9o7u
|
||||
|
||||
|
||||
|
||||
> 源码 (javascript)
|
||||
|
||||
``` javascript
|
||||
var strategy_version="1.0.0.0(new release 仅供学习研究使用,实盘后果自负。实盘策略访问http://pcclean.io/quant )";
|
||||
|
||||
var price_n={Futures_OKCoin_BSV_USD:2};
|
||||
var num_n={Futures_OKCoin_BSV_USD:0};
|
||||
var minestbuy={Futures_OKCoin_BSV_USD:1};
|
||||
var price_step={Futures_OKCoin_BSV_USD:0.05};
|
||||
var contract_min={Futures_OKCoin_BSV_USD:10};
|
||||
var wait_ms=3000;
|
||||
var max_wait_order=15000;
|
||||
var max_positions=4;
|
||||
|
||||
//gloabl variables
|
||||
var positions_duo=[];
|
||||
var positions_kong=[];
|
||||
var total_loop=0;
|
||||
var bet_duo=0;
|
||||
var bet_kong=0;
|
||||
|
||||
function main(){
|
||||
Log("strategy_version="+strategy_version);
|
||||
$.set_params(price_n,num_n,minestbuy,price_step,wait_ms,max_wait_order);
|
||||
while(true){
|
||||
var exname=exchange.GetName();
|
||||
var currency=exchange.GetCurrency();
|
||||
var account=_C(exchange.GetAccount);
|
||||
var ticker=_C(exchange.GetTicker);
|
||||
var depth=_C(exchange.GetDepth);
|
||||
var sell1=depth.Asks[0].Price;
|
||||
var buy1=depth.Bids[0].Price;
|
||||
var records=_C(exchange.GetRecords);
|
||||
if (records.length<=50){
|
||||
Log("records.length无效,跳过此次执行...");
|
||||
Sleep(wait_ms);
|
||||
continue;
|
||||
}
|
||||
var atr = TA.ATR(records, 20);
|
||||
if (atr.length<=1){
|
||||
Log("atr.length无效,跳过此次执行...");
|
||||
Sleep(wait_ms);
|
||||
continue;
|
||||
}
|
||||
var N=atr[atr.length-1];
|
||||
var position_unit=account.Stocks/(max_positions*2);
|
||||
var highest20=TA.Highest(records, 20, 'High');
|
||||
var lowest20=TA.Lowest(records, 20, 'Low');
|
||||
var highest10=TA.Highest(records, 10, 'High');
|
||||
var lowest10=TA.Lowest(records, 10, 'Low');
|
||||
|
||||
//建仓
|
||||
if (positions_duo.length===0){
|
||||
if (sell1<=lowest20){
|
||||
var heyuefenshu=_N(position_unit*sell1/contract_min[$.get_exchange_id(exchange)],0);
|
||||
exchange.SetDirection("buy");
|
||||
var dealamount=$.perform_limited_order("buy",exchange,sell1,heyuefenshu,false,"futures","buy");
|
||||
if (dealamount>0){
|
||||
var postion = {
|
||||
amount:dealamount,
|
||||
price:sell1,
|
||||
stoploss_price:sell1-2.5*N,
|
||||
stopwin_price:sell1+2.5*N,
|
||||
};
|
||||
positions_duo.push(postion);
|
||||
bet_duo=1;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (positions_kong.length===0){
|
||||
if (buy1>=highest20){
|
||||
var heyuefenshu=_N(position_unit*buy1/contract_min[$.get_exchange_id(exchange)],0);
|
||||
exchange.SetDirection("sell");
|
||||
var dealamount=$.perform_limited_order("buy",exchange,buy1,heyuefenshu,false,"futures","sell");
|
||||
if (dealamount>0){
|
||||
var postion = {
|
||||
amount:dealamount,
|
||||
price:buy1,
|
||||
stoploss_price:buy1+2.5*N,
|
||||
stopwin_price:buy1-2.5*N,
|
||||
};
|
||||
positions_kong.push(postion);
|
||||
bet_kong=1;
|
||||
}
|
||||
}
|
||||
}
|
||||
//加仓
|
||||
if (positions_duo.length>0){
|
||||
var last_price=positions_duo[positions_duo.length-1].price;
|
||||
if (bet_duo<max_positions){
|
||||
if (last_price-sell1>=0.5*N){
|
||||
var heyuefenshu=_N(position_unit*sell1/contract_min[$.get_exchange_id(exchange)],0);
|
||||
exchange.SetDirection("buy");
|
||||
var dealamount=$.perform_limited_order("buy",exchange,sell1,heyuefenshu,false,"futures","buy");
|
||||
if (dealamount>0){
|
||||
var postion = {
|
||||
amount:dealamount,
|
||||
price:sell1,
|
||||
stoploss_price:sell1-2.5*N,
|
||||
stopwin_price:sell1+2.5*N,
|
||||
};
|
||||
positions_duo.push(postion);
|
||||
bet_duo+=1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (positions_kong.length>0){
|
||||
var last_price=positions_kong[positions_kong.length-1].price;
|
||||
if (bet_kong<max_positions){
|
||||
if (buy1-last_price>=0.5*N){
|
||||
var heyuefenshu=_N(position_unit*buy1/contract_min[$.get_exchange_id(exchange)],0);
|
||||
exchange.SetDirection("sell");
|
||||
var dealamount=$.perform_limited_order("buy",exchange,buy1,heyuefenshu,false,"futures","sell");
|
||||
if (dealamount>0){
|
||||
var postion = {
|
||||
amount:dealamount,
|
||||
price:buy1,
|
||||
stoploss_price:buy1+2.5*N,
|
||||
stopwin_price:buy1-2.5*N,
|
||||
};
|
||||
positions_kong.push(postion);
|
||||
bet_kong+=1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//止损
|
||||
if (positions_duo.length>0){
|
||||
var positions_duo_new=[];
|
||||
for (var i=0; i < positions_duo.length; i++){
|
||||
if (buy1<=positions_duo[i].stoploss_price){
|
||||
exchange.SetDirection("closebuy");
|
||||
$.perform_limited_order("sell",exchange,buy1,positions_duo[i].amount,true,"futures","closebuy");
|
||||
}else{
|
||||
positions_duo_new.push(positions_duo[i]);
|
||||
}
|
||||
}
|
||||
positions_duo=positions_duo_new;
|
||||
}
|
||||
if (positions_kong.length>0){
|
||||
var positions_kong_new=[];
|
||||
for (var i=0; i < positions_kong.length; i++){
|
||||
if (sell1>=positions_kong[i].stoploss_price){
|
||||
exchange.SetDirection("closesell");
|
||||
$.perform_limited_order("sell",exchange,sell1,positions_kong[i].amount,true,"futures","closesell");
|
||||
}else{
|
||||
positions_kong_new.push(positions_kong[i]);
|
||||
}
|
||||
}
|
||||
positions_kong=positions_kong_new;
|
||||
}
|
||||
//止盈
|
||||
if (positions_duo.length>0){
|
||||
var positions_duo_new=[];
|
||||
for (var i=0; i < positions_duo.length; i++){
|
||||
if (buy1>=positions_duo[i].stopwin_price){
|
||||
exchange.SetDirection("closebuy");
|
||||
$.perform_limited_order("sell",exchange,buy1,positions_duo[i].amount,true,"futures","closebuy");
|
||||
}else{
|
||||
positions_duo_new.push(positions_duo[i]);
|
||||
}
|
||||
}
|
||||
positions_duo=positions_duo_new;
|
||||
}
|
||||
if (positions_kong.length>0){
|
||||
var positions_kong_new=[];
|
||||
for (var i=0; i < positions_kong.length; i++){
|
||||
if (sell1<=positions_kong[i].stopwin_price){
|
||||
exchange.SetDirection("closesell");
|
||||
$.perform_limited_order("sell",exchange,sell1,positions_kong[i].amount,true,"futures","closesell");
|
||||
}else{
|
||||
positions_kong_new.push(positions_kong[i]);
|
||||
}
|
||||
}
|
||||
positions_kong=positions_kong_new;
|
||||
}
|
||||
//清仓
|
||||
if (positions_duo.length>0){
|
||||
if (buy1>=highest10){
|
||||
for (var i=0; i < positions_duo.length; i++){
|
||||
exchange.SetDirection("closebuy");
|
||||
$.perform_limited_order("sell",exchange,buy1,positions_duo[i].amount,true,"futures","closebuy");
|
||||
}
|
||||
positions_duo=[];
|
||||
}
|
||||
}
|
||||
if (positions_kong.length>0){
|
||||
if (sell1<=lowest10){
|
||||
for (var i=0; i < positions_kong.length; i++){
|
||||
exchange.SetDirection("closesell");
|
||||
$.perform_limited_order("sell",exchange,sell1,positions_kong[i].amount,true,"futures","closesell");
|
||||
}
|
||||
positions_kong=[];
|
||||
}
|
||||
}
|
||||
//交易所强平
|
||||
var current_ok_position=_C(exchange.GetPosition);//must update here
|
||||
if (current_ok_position.length===0){
|
||||
positions_duo=[];
|
||||
positions_kong=[];
|
||||
}
|
||||
|
||||
//chart
|
||||
var table1={type: 'table', title: '期货仓位', cols: ['交易所','持仓量','冻结量','持仓均价','实现盈余','类型','合约代码'], rows: []};
|
||||
var table2={type: 'table', title: '跟踪仓位', cols: ['交易所','类型','数量','价格','止损价','止盈价'], rows: []};
|
||||
for (var i=0; i < current_ok_position.length; i++){
|
||||
table1.rows.push([exname,
|
||||
current_ok_position[i].Amount,
|
||||
current_ok_position[i].FrozenAmount,
|
||||
current_ok_position[i].Price,
|
||||
current_ok_position[i].Profit,
|
||||
current_ok_position[i].Type,
|
||||
current_ok_position[i].ContractType]);
|
||||
}
|
||||
for (i=0; i<positions_duo.length; ++i){
|
||||
table2.rows.push([exname,'做多',positions_duo[i].amount,positions_duo[i].price,
|
||||
positions_duo[i].stoploss_price,positions_duo[i].stopwin_price]);
|
||||
}
|
||||
for (i=0; i<positions_kong.length; ++i){
|
||||
table2.rows.push([exname,'做空',positions_kong[i].amount,positions_kong[i].price,
|
||||
positions_kong[i].stoploss_price,positions_kong[i].stopwin_price]);
|
||||
}
|
||||
LogStatus(
|
||||
'`' + JSON.stringify([table1,table2])+'`'+'\n'+
|
||||
'买多仓位:'+positions_duo.length+'\n'+
|
||||
'买空仓位:'+positions_kong.length+'\n'+
|
||||
'交易单位: '+position_unit+'\n'+
|
||||
'highest20: '+highest20+'\n'+
|
||||
'highest10: '+highest10+'\n'+
|
||||
'lowest20: '+lowest20+'\n'+
|
||||
'lowest10: '+lowest10+'\n'+
|
||||
'♜轮询次数: '+total_loop+'\n'+
|
||||
'♜更新时间: '+$.get_ChinaTimeString()+'\n'+
|
||||
'♜定制策略联系微信: alinwo(验证消息:botvs)。实盘策略访问: http://pcclean.io/quant'+'\n'
|
||||
);
|
||||
if (total_loop%300===0){
|
||||
LogProfit(account.Stocks);
|
||||
}
|
||||
|
||||
Sleep(wait_ms);
|
||||
total_loop++;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
> 策略出处
|
||||
|
||||
https://www.fmz.com/strategy/157372
|
||||
|
||||
> 更新时间
|
||||
|
||||
2019-07-16 14:57:23
|
||||
@@ -0,0 +1,405 @@
|
||||
|
||||
> 策略名称
|
||||
|
||||
【经典趋势策略】海龟交易策略
|
||||
|
||||
> 策略作者
|
||||
|
||||
zzx2019
|
||||
|
||||
> 策略描述
|
||||
|
||||
海龟交易策略是趋势策略中非常经典的交易策略,能在交易风险,仓位,资金利用率各方面做到很精细的控制。只要市场出现较大趋势(不管基于分钟K线,小时K线还是日K),海龟都会捕获到趋势并让利润最大化。此策略基于原版海龟交易法则实现,在2017年大牛市下取得了700%,远远高于大盘的收益。
|
||||
|
||||
策略特点:
|
||||
支持任意级别的趋势跟踪(分钟K,小时K,日K,周K等)
|
||||
支持任意交易对(ETH/BTC, BSV/BTC等)
|
||||
详细的策略报表(包括策略状态,交易历史记录等)
|
||||
支持10几个自定义个性化参数
|
||||
|
||||
参数说明:
|
||||
请查看 https://www.pcclean.io/z6zl
|
||||
|
||||
|
||||
|
||||
> 源码 (javascript)
|
||||
|
||||
``` javascript
|
||||
/*
|
||||
海龟策略
|
||||
用途:分散风险,平抑波动
|
||||
17:34 2018/9/12 retrySell后加sleep,解决清仓余额不足的问题
|
||||
17:58 2018/9/12 fixed Sell(-1, 0.00033): Less than minimum requirement
|
||||
8:58 2018/9/13 fixed 无限仓位问题
|
||||
11:12 2018/9/13 符合海龟交易法则
|
||||
20:23 2018/9/23 修改清仓时不更新account.stock的问题
|
||||
11:14 2018/10/18 retrysell函数支持 对最小数量的自动修正
|
||||
11:16 2018/10/19 支持utc+8时间和logprofit
|
||||
15:13 2018/10/19 支持对象化和管理多个交易对
|
||||
23:28 2018/10/20 支持统计手续费损失
|
||||
9:05 2018/10/22 retryBuy支持自动修正买入量
|
||||
22:10 2018/12/22 支持统计市价单盈亏
|
||||
11:40 2019/04/25 支持收益曲线连续
|
||||
*/
|
||||
|
||||
var ExchangProcessor={
|
||||
createNew: function(exc_obj){
|
||||
//策略参数
|
||||
var manage_assets=1;//bch
|
||||
var max_positions=4;//max=4N
|
||||
var price_n={BTC_BCH:8,ETH_BCH:8,XRP_BCH:8,EOS_BCH:8,LTC_BCH:8,DASH_BCH:8,CET_BCH:8}; //价格精度
|
||||
var num_n={BTC_BCH:8,ETH_BCH:8,XRP_BCH:8,EOS_BCH:8,LTC_BCH:8,DASH_BCH:8,CET_BCH:8}; //数量精度
|
||||
var minest_buy={BTC_BCH:0.001,ETH_BCH:0.01,XRP_BCH:1,EOS_BCH:1,LTC_BCH:0.1,DASH_BCH:0.01,CET_BCH:1};//最小买入量
|
||||
var minest_sell={BTC_BCH:0.001,ETH_BCH:0.01,XRP_BCH:1,EOS_BCH:1,LTC_BCH:0.1,DASH_BCH:0.01,CET_BCH:1};//最小卖出量
|
||||
var order_wait_secs=120000; //订单的最长等待时间 毫秒
|
||||
var wait_ms=1000;//默认等待毫秒
|
||||
var sxf=0.0005;//用来计算手续费消耗
|
||||
|
||||
|
||||
//全局状态变量
|
||||
var positions=[];//记录仓位
|
||||
var init_asset=0; //初始资产
|
||||
var trades=[];//所有交易
|
||||
var trades_recorder=true;//记录所有交易
|
||||
var pre_time=null; //记录轮询间隔时间
|
||||
var approximate_profit=0;//盈亏近似值
|
||||
var add_already=0;//已经加仓次数
|
||||
|
||||
var processor={};
|
||||
//重试购买,直到成功返回
|
||||
processor.retryBuy=function(ex,price,num)
|
||||
{
|
||||
var currency=ex.GetCurrency();
|
||||
var r=ex.Buy(_N(price,price_n[currency]), _N(num,num_n[currency]));
|
||||
while (!r){
|
||||
Log("Buy失败,正在retry。");
|
||||
Sleep(wait_ms);
|
||||
var account=_C(ex.GetAccount);
|
||||
var ticker=_C(ex.GetTicker);
|
||||
var last=ticker.Last;
|
||||
var fixedAmount=(price===-1?Math.min(account.Balance*0.95,num):Math.min(account.Balance/last*0.95,num));
|
||||
r=ex.Buy(_N(price,price_n[currency]), _N(fixedAmount,num_n[currency]));
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
//重试卖出,直到成功返回
|
||||
processor.retrySell=function(ex,price,num){
|
||||
var currency=ex.GetCurrency();
|
||||
var r=ex.Sell(_N(price,price_n[currency]), _N(num,num_n[currency]));
|
||||
while (!r){
|
||||
Log("Sell失败,正在retry。");
|
||||
Sleep(wait_ms);
|
||||
var account=_C(ex.GetAccount);
|
||||
var fixedAmount=Math.min(account.Stocks,num);
|
||||
r=ex.Sell(_N(price,price_n[currency]), _N(fixedAmount,num_n[currency]));
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
processor.get_ChinaTimeString=function(){
|
||||
var date = new Date();
|
||||
var now_utc = Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate(),
|
||||
date.getUTCHours(), date.getUTCMinutes(), date.getUTCSeconds());
|
||||
var cdate=new Date(now_utc);
|
||||
cdate.setHours(cdate.getHours()+8);
|
||||
var localstring=cdate.getFullYear()+'/'+(cdate.getMonth()+1)+'/'+cdate.getDate()+' '+cdate.getHours()+':'+cdate.getMinutes()+':'+cdate.getSeconds();
|
||||
return localstring;
|
||||
}
|
||||
|
||||
processor.init_obj=function(){
|
||||
_CDelay(wait_ms);
|
||||
pre_time = new Date();
|
||||
|
||||
//init
|
||||
{
|
||||
var account=_C(exc_obj.GetAccount);
|
||||
var ticker=_C(exc_obj.GetTicker);
|
||||
var last=ticker.Last;
|
||||
init_asset=(account.Balance+account.FrozenBalance)+(account.Stocks+account.FrozenStocks)*last;
|
||||
Sleep(wait_ms);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
processor.work=function(){
|
||||
var cur_time = new Date();
|
||||
var passedtime=cur_time-pre_time;
|
||||
pre_time=cur_time;
|
||||
|
||||
//计算n,头寸
|
||||
var exname=exc_obj.GetName();
|
||||
var currency=exc_obj.GetCurrency();
|
||||
var account=_C(exc_obj.GetAccount);
|
||||
var ticker=_C(exc_obj.GetTicker);
|
||||
var depth = _C(exc_obj.GetDepth);
|
||||
var last=ticker.Last;
|
||||
var ask1=depth.Asks[0].Price;
|
||||
var bid1=depth.Bids[0].Price;
|
||||
var bestprice=bid1+(Math.abs(ask1-bid1)/2);
|
||||
var records = _C(exc_obj.GetRecords);
|
||||
if (records.length<=50){
|
||||
Log("records.length is not valid.");
|
||||
Sleep(wait_ms);
|
||||
return;
|
||||
}
|
||||
var atr = TA.ATR(records, 20);
|
||||
if (atr.length<=1){
|
||||
Log("atr.length is not valid.");
|
||||
Sleep(wait_ms);
|
||||
return;
|
||||
}
|
||||
var N=atr[atr.length-1];
|
||||
var position_unit=Math.min(manage_assets*0.01/N,account.Balance/last*0.95);//cet
|
||||
//Log("N="+N+", 头寸单位="+position_unit+"CET");
|
||||
var highest=TA.Highest(records, 20, 'High');
|
||||
var Lowest=TA.Lowest(records, 10, 'Low');
|
||||
var cur_asset=(account.Balance+account.FrozenBalance)+(account.Stocks+account.FrozenStocks)*last;
|
||||
var rsi6 = TA.RSI(records, 6);
|
||||
var rsi12 = TA.RSI(records, 12);
|
||||
if (rsi6.length<=5 || rsi12.length<=5){
|
||||
Log("rsi is not valid.");
|
||||
Sleep(wait_ms);
|
||||
return;
|
||||
}
|
||||
var rsi_in=false;
|
||||
if (rsi6[rsi6.length-1]-rsi6[rsi6.length-2]>5 &&
|
||||
rsi6[rsi6.length-3]-rsi6[rsi6.length-2]>5 &&
|
||||
rsi6[rsi6.length-2]<=55 &&
|
||||
rsi6[rsi6.length-1]>rsi12[rsi12.length-1]){
|
||||
//Log("rsi_in=true");
|
||||
rsi_in=true;
|
||||
}
|
||||
var rsi_out=false;
|
||||
if (rsi6[rsi6.length-2]-rsi6[rsi6.length-1]>5 &&
|
||||
rsi6[rsi6.length-2]>=70){
|
||||
//Log("rsi_out=true");
|
||||
rsi_out=true;
|
||||
}
|
||||
|
||||
//建仓
|
||||
if (positions.length==0 && position_unit>minest_buy[currency]){
|
||||
if (last>=highest)
|
||||
{
|
||||
var buyID = processor.retryBuy(exc_obj,last,position_unit);
|
||||
Sleep(order_wait_secs);
|
||||
var buyOrder=_C(exc_obj.GetOrder,buyID);
|
||||
if (buyOrder.Status!=ORDER_STATE_CLOSED){
|
||||
exc_obj.CancelOrder(buyID);
|
||||
}
|
||||
if (buyOrder.DealAmount>0){
|
||||
var postion = {
|
||||
amount:buyOrder.DealAmount,
|
||||
buy_price:buyOrder.AvgPrice,
|
||||
stoploss_price:buyOrder.AvgPrice-2*N};
|
||||
positions.push(postion);
|
||||
|
||||
var details={
|
||||
type:"建仓",
|
||||
time:processor.get_ChinaTimeString(),
|
||||
RealAmount:buyOrder.DealAmount,
|
||||
WantAmount:position_unit,
|
||||
RealPrice:buyOrder.AvgPrice,
|
||||
WantPrice:buyOrder.Price,
|
||||
Memo:""
|
||||
};
|
||||
if (trades_recorder){
|
||||
trades.push(details);
|
||||
}
|
||||
|
||||
add_already=1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//加仓
|
||||
if (positions.length>0 && position_unit>minest_buy[currency]){
|
||||
var last_buy_price=positions[positions.length-1].buy_price;
|
||||
if (add_already<max_positions){//max = 4N
|
||||
if (last-last_buy_price>=0.5*N){
|
||||
var buyID = processor.retryBuy(exc_obj,last,position_unit);
|
||||
Sleep(order_wait_secs);
|
||||
var buyOrder=_C(exc_obj.GetOrder,buyID);
|
||||
if (buyOrder.Status!=ORDER_STATE_CLOSED){
|
||||
exc_obj.CancelOrder(buyID);
|
||||
}
|
||||
if (buyOrder.DealAmount>0){
|
||||
var postion = {
|
||||
amount:buyOrder.DealAmount,
|
||||
buy_price:buyOrder.AvgPrice,
|
||||
stoploss_price:buyOrder.AvgPrice-2*N};
|
||||
positions.push(postion);
|
||||
|
||||
var details={
|
||||
type:"加仓",
|
||||
time:processor.get_ChinaTimeString(),
|
||||
RealAmount:buyOrder.DealAmount,
|
||||
WantAmount:position_unit,
|
||||
RealPrice:buyOrder.AvgPrice,
|
||||
WantPrice:last,
|
||||
Memo:""
|
||||
};
|
||||
if (trades_recorder){
|
||||
trades.push(details);
|
||||
}
|
||||
|
||||
add_already=add_already+1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//止损
|
||||
if (positions.length>0){
|
||||
var positions_new=[];
|
||||
for (var i=0; i < positions.length; i++){
|
||||
if (last<=positions[i].stoploss_price){
|
||||
account=_C(exc_obj.GetAccount);
|
||||
var fixedAmount=Math.min(account.Stocks,positions[i].amount);
|
||||
if (fixedAmount>minest_sell[currency]){
|
||||
var sellID = processor.retrySell(exc_obj, last, fixedAmount);
|
||||
Sleep(order_wait_secs);
|
||||
var sellOrder=_C(exc_obj.GetOrder,sellID);
|
||||
approximate_profit+=(sellOrder.AvgPrice*sellOrder.DealAmount*(1-sxf)-positions[i].buy_price*sellOrder.DealAmount*(1+sxf));
|
||||
Log("定价卖出: 数量-"+sellOrder.DealAmount+",approximate_profit="+approximate_profit);
|
||||
if (sellOrder.Status!=ORDER_STATE_CLOSED){
|
||||
exc_obj.CancelOrder(sellID);
|
||||
if (Math.min(account.Stocks,fixedAmount-sellOrder.DealAmount)>minest_sell[currency]){
|
||||
var marketsellOrderID=processor.retrySell(exc_obj, -1, fixedAmount-sellOrder.DealAmount);
|
||||
Sleep(order_wait_secs);
|
||||
var marketsellOrderData=_C(exc_obj.GetOrder,marketsellOrderID);
|
||||
approximate_profit+=(marketsellOrderData.AvgPrice*marketsellOrderData.DealAmount*(1-sxf)-positions[i].buy_price*marketsellOrderData.DealAmount*(1+sxf));
|
||||
Log("市价卖出: 数量-"+marketsellOrderData.DealAmount+",approximate_profit="+approximate_profit);
|
||||
}
|
||||
}
|
||||
|
||||
var details={
|
||||
type:"止损",
|
||||
time:processor.get_ChinaTimeString(),
|
||||
RealAmount:-1,
|
||||
WantAmount:fixedAmount,
|
||||
RealPrice:-1,
|
||||
WantPrice:last,
|
||||
Memo:(last>positions[i].buy_price?"盈利":"亏损")
|
||||
};
|
||||
if (trades_recorder){
|
||||
trades.push(details);
|
||||
}
|
||||
}
|
||||
}else{
|
||||
positions_new.push(positions[i]);
|
||||
}
|
||||
}
|
||||
positions=positions_new;
|
||||
}
|
||||
|
||||
//清仓
|
||||
if (positions.length>0){
|
||||
if (last<=Lowest){
|
||||
var positions_new=[];
|
||||
for (var i=0; i < positions.length; i++){
|
||||
account=_C(exc_obj.GetAccount);
|
||||
var fixedAmount=Math.min(account.Stocks,positions[i].amount);
|
||||
if (fixedAmount>minest_sell[currency]){
|
||||
var sellID = processor.retrySell(exc_obj, last, fixedAmount);
|
||||
Sleep(order_wait_secs);
|
||||
var sellOrder=_C(exc_obj.GetOrder,sellID);
|
||||
approximate_profit+=(sellOrder.AvgPrice*sellOrder.DealAmount*(1-sxf)-positions[i].buy_price*sellOrder.DealAmount*(1+sxf));
|
||||
Log("定价卖出: 数量-"+sellOrder.DealAmount+",approximate_profit="+approximate_profit);
|
||||
if (sellOrder.Status!=ORDER_STATE_CLOSED){
|
||||
exc_obj.CancelOrder(sellID);
|
||||
if (Math.min(account.Stocks,fixedAmount-sellOrder.DealAmount)>minest_sell[currency]){
|
||||
var marketsellOrderID=processor.retrySell(exc_obj, -1, fixedAmount-sellOrder.DealAmount);
|
||||
Sleep(order_wait_secs);
|
||||
var marketsellOrderData=_C(exc_obj.GetOrder,marketsellOrderID);
|
||||
approximate_profit+=(marketsellOrderData.AvgPrice*marketsellOrderData.DealAmount*(1-sxf)-positions[i].buy_price*marketsellOrderData.DealAmount*(1+sxf));
|
||||
Log("市价卖出: 数量-"+marketsellOrderData.DealAmount+",approximate_profit="+approximate_profit);
|
||||
}
|
||||
}
|
||||
|
||||
var details={
|
||||
type:"清仓",
|
||||
time:processor.get_ChinaTimeString(),
|
||||
RealAmount:-1,
|
||||
WantAmount:fixedAmount,
|
||||
RealPrice:-1,
|
||||
WantPrice:last,
|
||||
Memo:(last>positions[i].buy_price?"盈利":"亏损")
|
||||
};
|
||||
if (trades_recorder){
|
||||
trades.push(details);
|
||||
}
|
||||
}
|
||||
}
|
||||
positions=positions_new;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//显示状态
|
||||
var table1 = {type: 'table', title: '仓位-'+exname+'('+currency+')', cols: ['数量', '成交价','止损价'], rows: []};
|
||||
var table2 = {type: 'table', title: '状态-'+exname+'('+currency+')', cols: ['平均真实波幅(N)','头寸单位','初始资产','当前资产','轮询时间','最新价','Highest','Lowest','加仓次数','【近似盈亏】'], rows: []};
|
||||
var table3 = {type: 'table', title: '交易历史-'+exname+'('+currency+')', cols: ['日期','类型', '成交数量','发单数量','成交价','发单价','备注'], rows: []};
|
||||
for (var i=0; i < positions.length; i++){
|
||||
table1.rows.push([positions[i].amount,positions[i].buy_price,positions[i].stoploss_price]);
|
||||
}
|
||||
table2.rows.push([N,position_unit,init_asset,cur_asset,passedtime+'ms',last,highest,Lowest,add_already,approximate_profit]);
|
||||
for (i=0; i < trades.length; i++){
|
||||
table3.rows.push([trades[i].time,trades[i].type,trades[i].RealAmount,trades[i].WantAmount,trades[i].RealPrice,trades[i].WantPrice,trades[i].Memo]);
|
||||
}
|
||||
processor.logstatus=('`' + JSON.stringify([table1, table2, table3])+'`'+'\n');
|
||||
|
||||
//记录盈利
|
||||
processor.logprofit=approximate_profit;
|
||||
|
||||
//rest
|
||||
Sleep(wait_ms);
|
||||
}
|
||||
|
||||
return processor;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
//主函数
|
||||
function main(){
|
||||
var exchange_num=exchanges.length;
|
||||
var processors=[];
|
||||
for (var i=0; i<exchange_num; ++i){
|
||||
var p=ExchangProcessor.createNew(exchanges[i]);
|
||||
processors.push(p);
|
||||
}
|
||||
for (i=0; i<exchange_num; ++i){
|
||||
processors[i].init_obj();
|
||||
}
|
||||
var pre_profit=Number(_G("pre_profit"));
|
||||
Log('之前收入累计:'+pre_profit);
|
||||
var lastprofit=0;
|
||||
while(true){
|
||||
var allstatus='策略仅作为学习使用,实盘风险自担。#0000ff'+'\n';
|
||||
var allprofit=0;
|
||||
for (i=0; i<exchange_num; ++i){
|
||||
processors[i].work();
|
||||
allstatus+=processors[i].logstatus;
|
||||
allprofit+=processors[i].logprofit;
|
||||
}
|
||||
allstatus+=('微信:alinwo (验证消息: botvs)'+'\n');
|
||||
LogStatus(allstatus);
|
||||
if (lastprofit!==allprofit){
|
||||
LogProfit(pre_profit+allprofit);
|
||||
_G("pre_profit", pre_profit+allprofit);
|
||||
lastprofit=allprofit;
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
> 策略出处
|
||||
|
||||
https://www.fmz.com/strategy/157270
|
||||
|
||||
> 更新时间
|
||||
|
||||
2019-07-16 14:07:22
|
||||
@@ -0,0 +1,449 @@
|
||||
|
||||
> 策略名称
|
||||
|
||||
做市策略(basefex)
|
||||
|
||||
> 策略作者
|
||||
|
||||
daniaoren
|
||||
|
||||
> 策略描述
|
||||
|
||||
自用做市策略备份
|
||||
|
||||
不能直接用,需配合自己的keygen模块生成私钥
|
||||
|
||||
|
||||
|
||||
> 源码 (python)
|
||||
|
||||
``` python
|
||||
import requests
|
||||
import json
|
||||
import time
|
||||
import logging
|
||||
from tenacity import *
|
||||
from keygen import *
|
||||
|
||||
TELEGRAM = False
|
||||
if len(sys.argv) > 1:
|
||||
if sys.argv[1] == 'telegram':
|
||||
TELEGRAM = True
|
||||
if TELEGRAM:
|
||||
from telegram_bot import *
|
||||
else:
|
||||
def sendBotMessage(msg):
|
||||
print ('telegram:', msg)
|
||||
logger.debug('telegram: %s', msg)
|
||||
def getNewMessage():
|
||||
return ''
|
||||
|
||||
global LOGGING_LEVEL
|
||||
#LOGGING_LEVEL = logging.DEBUG
|
||||
LOGGING_LEVEL = logging.INFO
|
||||
|
||||
baseurl = 'https://api.basefex.com'
|
||||
slidePrice = 1
|
||||
minDiff = 10 #最小做市差价
|
||||
minAmount = 30 #最小下单量
|
||||
balancePosition = 200 #仓位为多少时考虑平衡减仓或市价减仓
|
||||
balanceDiff = 12 #差价为多少时考虑平衡减仓
|
||||
immediateBalanceDiff = 50 #差价为多少时市价减仓
|
||||
liquidatePriceAlertLow = 4000 #强平危险区下限
|
||||
liquidatePriceAlertHigh = 20000 #强平危险区上限
|
||||
lastBuyPrice = 0
|
||||
lastSellPrice = 0
|
||||
lastBuyAmount = 0
|
||||
lastSellAmount = 0
|
||||
currentBalance = 0
|
||||
currentPosition = []
|
||||
initBalance = 0
|
||||
tradeList = []
|
||||
totalBuyAmount = 0
|
||||
totalBuyPosition = 0
|
||||
totalSellAmount = 0
|
||||
totalSellPosition = 0
|
||||
totalBuyCount = 0
|
||||
totalSellCount = 0
|
||||
|
||||
@retry(stop=(stop_after_delay(10) | stop_after_attempt(5)) , wait=wait_fixed(2))
|
||||
def safeRequest(url, hed):
|
||||
response = requests.get(url, headers=hed)
|
||||
return response
|
||||
|
||||
@retry(stop=(stop_after_delay(10) | stop_after_attempt(5)) , wait=wait_fixed(2))
|
||||
def safeRequestPost(url, hed, payload):
|
||||
response = requests.post(url, headers=hed, json=payload)
|
||||
return response
|
||||
|
||||
@retry(stop=(stop_after_delay(10) | stop_after_attempt(5)) , wait=wait_fixed(2))
|
||||
def safeRequestDelete(url, hed):
|
||||
response = requests.delete(url, headers=hed)
|
||||
return response
|
||||
|
||||
def getAccountInfo():
|
||||
uri = '/accounts'
|
||||
auth_token = generate_token(key_id, private_key, "GET", 1584014794, uri)
|
||||
hed = {'authorization': 'Bearer ' + auth_token}
|
||||
response = safeRequest(baseurl+uri, hed)
|
||||
return response.json()
|
||||
|
||||
def updateAccount():
|
||||
global currentBalance
|
||||
global currentPosition
|
||||
accountInfo = getAccountInfo()[0]
|
||||
currentBalance = accountInfo['cash']['balances']
|
||||
for position in accountInfo['positions']:
|
||||
if position['symbol'] == 'BTCUSD':
|
||||
currentPosition = position
|
||||
break
|
||||
print('CURRENT POSITION: ', currentPosition['entryPrice'], currentPosition['size'])
|
||||
print('CURRENT BALANCE: ', currentBalance, 'PROFIT: ', currentBalance - initBalance)
|
||||
logger.info('CURRENT BALANCE: %s | POSITION %s@%s', currentBalance, currentPosition['size'], currentPosition['entryPrice'])
|
||||
logger.info('PROFIT: %s', currentBalance - initBalance)
|
||||
return accountInfo
|
||||
|
||||
def getTradeList(limit):
|
||||
uri = '/trades'
|
||||
query = '?limit=' + str(limit)
|
||||
auth_token = generate_token(key_id, private_key, "GET", 1584014794, uri, query)
|
||||
hed = {'authorization': 'Bearer ' + auth_token}
|
||||
response = safeRequest(baseurl+uri+query, hed)
|
||||
return response.json()
|
||||
|
||||
def getCurrentBestPrices():
|
||||
uri = '/depth@BTCUSD/snapshot'
|
||||
auth_token = generate_token(key_id, private_key, "GET", 1584014794, uri)
|
||||
hed = {'authorization': 'Bearer ' + auth_token}
|
||||
response = safeRequest(baseurl+uri, hed)
|
||||
return response.json()['bestPrices']
|
||||
|
||||
def placeOrder(price, amount, side):
|
||||
if amount == 0:
|
||||
print ("amount is zero return")
|
||||
return
|
||||
payload = {'price': price, 'size': amount, 'type': 'LIMIT', 'side': side, 'symbol': 'BTCUSD'}
|
||||
print (payload)
|
||||
uri = '/orders'
|
||||
auth_token = generate_token(key_id, private_key, "POST", 1584014794, "/orders", "", json.dumps(payload))
|
||||
hed = {'authorization': 'Bearer ' + auth_token, 'Content-Type': 'application/json'}
|
||||
response = safeRequestPost(baseurl+uri, hed, payload)
|
||||
# 如果下单失败,报错返回
|
||||
if (str(response) != '<Response [200]>'):
|
||||
print (str(response))
|
||||
logger.error('PLACE ORDER ERROR: %s', str(response))
|
||||
sendBotMessage('PLACE ORDER ERROR: ' + str(response))
|
||||
return
|
||||
# 下单成功,记录相关信息
|
||||
if side == 'BUY':
|
||||
global lastBuyPrice
|
||||
global lastBuyAmount
|
||||
lastBuyPrice = price
|
||||
lastBuyAmount = amount
|
||||
elif side == 'SELL':
|
||||
global lastSellPrice
|
||||
global lastSellAmount
|
||||
lastSellPrice = price
|
||||
lastSellAmount = amount
|
||||
else:
|
||||
logger.error('ERROR: unexpected side')
|
||||
sendBotMessage('ERROR: unexpected side')
|
||||
return response.json()
|
||||
|
||||
def cancelOrder(id):
|
||||
uri = '/orders/'+id
|
||||
auth_token = generate_token(key_id, private_key, "DELETE", 1584014794, uri)
|
||||
hed = {'authorization': 'Bearer ' + auth_token}
|
||||
response = safeRequestDelete(baseurl+uri, hed)
|
||||
return response
|
||||
|
||||
def getOpeningOrderCount():
|
||||
uri = '/orders/opening/count'
|
||||
query = '?symbol=BTCUSD'
|
||||
auth_token = generate_token(key_id, private_key, "GET", 1584014794, uri, query)
|
||||
hed = {'authorization': 'Bearer ' + auth_token}
|
||||
response = safeRequest(baseurl+uri+query, hed)
|
||||
return response.json()
|
||||
|
||||
def getOpeningOrderList():
|
||||
uri = '/orders/opening'
|
||||
query = '?symbol=BTCUSD'
|
||||
auth_token = generate_token(key_id, private_key, "GET", 1584014794, uri, query)
|
||||
hed = {'authorization': 'Bearer ' + auth_token}
|
||||
response = safeRequest(baseurl+uri+query, hed)
|
||||
return response.json()
|
||||
|
||||
def getOrderList():
|
||||
uri = '/orders'
|
||||
query = '?symbol=BTCUSD'
|
||||
auth_token = generate_token(key_id, private_key, "GET", 1584014794, uri, query)
|
||||
hed = {'authorization': 'Bearer ' + auth_token}
|
||||
response = safeRequest(baseurl+uri+query, hed)
|
||||
return response.json()
|
||||
|
||||
def cancelPendingOrders():
|
||||
orderList = getOpeningOrderList()
|
||||
if len(orderList) <= 0:
|
||||
return
|
||||
for order in orderList:
|
||||
print('canceled:',order['id'])
|
||||
cancelOrder(order['id'])
|
||||
return
|
||||
|
||||
def cancelPendingBuyOrders():
|
||||
orderList = getOpeningOrderList()
|
||||
if len(orderList) <= 0:
|
||||
return
|
||||
for order in orderList:
|
||||
if order['side'] == 'BUY':
|
||||
cancelOrder(order['id'])
|
||||
return
|
||||
|
||||
def cancelPendingSellOrders():
|
||||
orderList = getOpeningOrderList()
|
||||
if len(orderList) <= 0:
|
||||
return
|
||||
for order in orderList:
|
||||
if order['side'] == 'SELL':
|
||||
cancelOrder(order['id'])
|
||||
return
|
||||
|
||||
def setLogger(loggerName):
|
||||
logger = logging.getLogger(loggerName)
|
||||
global LOGGING_LEVEL
|
||||
logger.setLevel(LOGGING_LEVEL)
|
||||
|
||||
fileHandler = logging.FileHandler(loggerName+'.log', mode='a')
|
||||
formatter = logging.Formatter('%(asctime)s - %(message)s')
|
||||
fileHandler.setFormatter(formatter)
|
||||
logger.addHandler(fileHandler)
|
||||
return logger
|
||||
|
||||
def onTick():
|
||||
#初始化状态及 确认价位
|
||||
global lastBuyPrice, lastSellPrice, lastBuyAmount, lastSellAmount
|
||||
cancelBuyOrder = False
|
||||
cancelSellOrder = False
|
||||
result = getCurrentBestPrices()
|
||||
bestAsk = result['ask']
|
||||
bestBid = result['bid']
|
||||
print('bestBid',bestBid,'bestAsk',bestAsk)
|
||||
logger.info('bestBid: %s|bestAsk: %s',bestBid,bestAsk)
|
||||
logger.info('LastBuy %s@%s | LastSell %s@%s', lastBuyAmount,lastBuyPrice,lastSellAmount,lastSellPrice)
|
||||
buyPrice = bestBid + slidePrice
|
||||
sellPrice = bestAsk - slidePrice
|
||||
#确认仓位
|
||||
global currentPosition
|
||||
buyAmountAdjust = 0
|
||||
sellAmountAdjust = 0
|
||||
|
||||
#####平衡模块####
|
||||
#是否满足市价平仓条件,满足直接尝试平仓并返回
|
||||
#平仓条件:仓位大于balancePosition, 价差大于immediateBalanceDiff
|
||||
#买单方向
|
||||
if (currentPosition['size'] > balancePosition):
|
||||
if bestBid - currentPosition['entryPrice'] > immediateBalanceDiff:
|
||||
cancelPendingOrders()
|
||||
placeOrder(bestBid, abs(currentPosition['size']), 'SELL')
|
||||
print('immediateBalance', currentPosition)
|
||||
sendBotMessage('IMMEDIATE BALANCE\n' + "Position: "+str(currentPosition['size'])+"@"+str(currentPosition['entryPrice'])+"\nBest Bid: " + str(bestBid))
|
||||
sleep(3)
|
||||
cancelPendingOrders()
|
||||
updateAccount()
|
||||
return
|
||||
#卖单方向
|
||||
elif ( currentPosition['size'] < -balancePosition):
|
||||
if currentPosition['entryPrice'] - bestAsk > immediateBalanceDiff:
|
||||
cancelPendingOrders()
|
||||
placeOrder(bestAsk, abs(currentPosition['size']), 'BUY')
|
||||
print('immediateBalance', currentPosition)
|
||||
sendBotMessage('IMMEDIATE BALANCE\n' + "Position: "+str(currentPosition['size'])+"@"+str(currentPosition['entryPrice'])+"\nBest Ask: " + str(bestAsk))
|
||||
sleep(3)
|
||||
cancelPendingOrders()
|
||||
updateAccount()
|
||||
return
|
||||
|
||||
#不能市价平仓,调整买卖单数量偏向减仓
|
||||
#买单方向
|
||||
if (currentPosition['size'] > balancePosition):
|
||||
if sellPrice - currentPosition['entryPrice'] > balanceDiff:
|
||||
sellAmountAdjust = int(currentPosition['size']/10)
|
||||
print('adjust buy/sell', buyAmountAdjust,sellAmountAdjust)
|
||||
#卖单方向
|
||||
elif ( currentPosition['size'] < -balancePosition):
|
||||
if currentPosition['entryPrice'] - buyPrice >balanceDiff:
|
||||
buyAmountAdjust = int(currentPosition['size'] / -10)
|
||||
print('adjust buy/sell', buyAmountAdjust,sellAmountAdjust)
|
||||
|
||||
#确认是否进入强平危险区
|
||||
#强制平衡时只能单方面下单
|
||||
liquidatePrice = currentPosition['liquidatePrice']
|
||||
if liquidatePrice > liquidatePriceAlertLow and currentPosition['size'] > balancePosition:
|
||||
logger.info('Liquidate risk ONLY CAN SELL')
|
||||
buyAmountAdjust = -minAmount
|
||||
elif liquidatePrice < liquidatePriceAlertHigh and currentPosition['size'] < -balancePosition:
|
||||
logger.info('Liquidate risk ONLY CAN BUY')
|
||||
sellAmountAdjust = -minAmount
|
||||
####平衡模块结束####
|
||||
|
||||
####做市下单模块####
|
||||
#差价不满足条件,取消所有订单并返回
|
||||
if sellPrice - buyPrice < minDiff:
|
||||
cancelPendingOrders()
|
||||
return
|
||||
|
||||
#确认上次的订单价格和现价差距,决定是否要调整买卖单
|
||||
if (lastBuyPrice > 0) and (abs(buyPrice - lastBuyPrice) > slidePrice):
|
||||
cancelBuyOrder = True
|
||||
if (lastSellPrice > 0) and (abs(lastSellPrice - sellPrice) > slidePrice):
|
||||
cancelSellOrder = True
|
||||
|
||||
#买单卖单均需调整,取消所有后调整
|
||||
if cancelBuyOrder & cancelSellOrder:
|
||||
logger.debug('CANCEL ALL')
|
||||
cancelPendingOrders()
|
||||
placeOrder(buyPrice, minAmount + buyAmountAdjust, 'BUY')
|
||||
placeOrder(sellPrice, minAmount + sellAmountAdjust, 'SELL')
|
||||
return
|
||||
|
||||
#按单数量分情况处理
|
||||
openingOrderCount = getOpeningOrderCount()['count']
|
||||
logger.debug('openingOrderCount %s', openingOrderCount)
|
||||
#单数超过2不正常,退出
|
||||
if openingOrderCount > 2:
|
||||
print('openingOrderCount:',openingOrderCount,'error')
|
||||
logger.error('ERROR openingOrderCount: %s', str(openingOrderCount))
|
||||
sendBotMessage('ERROR openingOrderCount: ' + str(openingOrderCount))
|
||||
exit()
|
||||
#单数为2,旧单都在,看需要更新买还是卖
|
||||
elif openingOrderCount == 2:
|
||||
if cancelBuyOrder:
|
||||
logger.debug('CANCEL BUY')
|
||||
cancelPendingBuyOrders()
|
||||
placeOrder(buyPrice, minAmount + buyAmountAdjust, 'BUY')
|
||||
elif cancelSellOrder:
|
||||
logger.debug('CANCEL SELL')
|
||||
cancelPendingSellOrders()
|
||||
placeOrder(sellPrice, minAmount + sellAmountAdjust, 'SELL')
|
||||
else:
|
||||
logger.debug('CANCEL NONE')
|
||||
#单数为1,看具体生效的是买单还是卖单再更新
|
||||
elif openingOrderCount == 1:
|
||||
#看是买单还是卖单
|
||||
orderList = getOpeningOrderList()
|
||||
#更新的过程中有新单,跳过,重新check
|
||||
if len(orderList) != 1:
|
||||
logger.debug('openingOrderCount changed!! skip')
|
||||
return
|
||||
side = orderList[0]['side']
|
||||
logger.info('only 1 order remain: %s', side)
|
||||
#现在有效的是买单
|
||||
if side == 'BUY':
|
||||
#如果买单无效,那么更新买单
|
||||
if cancelBuyOrder:
|
||||
cancelPendingBuyOrders()
|
||||
placeOrder(buyPrice, minAmount + buyAmountAdjust, 'BUY')
|
||||
#卖单无论如何都要更新
|
||||
placeOrder(sellPrice, minAmount + sellAmountAdjust, 'SELL')
|
||||
#现在有效的是卖单
|
||||
elif side == 'SELL':
|
||||
#如果卖单无效,那么更新卖单
|
||||
if cancelSellOrder:
|
||||
cancelPendingSellOrders()
|
||||
placeOrder(sellPrice, minAmount + sellAmountAdjust, 'SELL')
|
||||
#买单无论如何都要更新
|
||||
placeOrder(buyPrice, minAmount + buyAmountAdjust, 'BUY')
|
||||
else:
|
||||
print('unexpected side, exit')
|
||||
exit()
|
||||
#单数为0,无论如何都需要补单
|
||||
elif openingOrderCount == 0:
|
||||
placeOrder(buyPrice, minAmount + buyAmountAdjust, 'BUY')
|
||||
placeOrder(sellPrice, minAmount + sellAmountAdjust, 'SELL')
|
||||
####做市下单模块结束####
|
||||
return
|
||||
|
||||
###### TEST AREA
|
||||
#exit()
|
||||
###### INIT ######
|
||||
#SETUP LOGGER
|
||||
logger = setLogger('merTradeLog')
|
||||
#CANCEL ALL ORDER
|
||||
cancelPendingOrders()
|
||||
#GET ACCOUNT INFO
|
||||
updateAccount()
|
||||
accountInfo = getAccountInfo()[0]
|
||||
initBalance = currentBalance
|
||||
print('initBalance:', initBalance)
|
||||
tradeList = getTradeList(1);
|
||||
tickCount = 0
|
||||
startTime = time.time()
|
||||
while True:
|
||||
#Telegram控制模块
|
||||
message = getNewMessage()
|
||||
if message == '!':
|
||||
cancelPendingOrders()
|
||||
updateAccount()
|
||||
sendBotMessage('Balance: '+str(currentBalance)+"\nPosition: "+str(currentPosition['size'])+"@"+str(currentPosition['entryPrice'])+"\nLiquidatePrice: "+str(currentPosition['liquidatePrice']) +"\nProfit: " + str(currentBalance - initBalance))
|
||||
sendBotMessage('STOP STRATEGY')
|
||||
exit()
|
||||
elif message == '1':
|
||||
updateAccount()
|
||||
sendBotMessage('Balance: '+str(currentBalance)+"\nPosition: "+str(currentPosition['size'])+"@"+str(currentPosition['entryPrice'])+"\nLiquidatePrice: "+str(currentPosition['liquidatePrice']) +"\nProfit: " + str(currentBalance - initBalance))
|
||||
elif message == '2':
|
||||
if totalBuyAmount != 0:
|
||||
sendBotMessage('Buy position: ' + str(totalBuyAmount) + '@' + str(totalBuyPosition/totalBuyAmount))
|
||||
if totalSellAmount != 0:
|
||||
sendBotMessage('Sell position: ' + str(totalSellAmount) + '@' + str(totalSellPosition/totalSellAmount))
|
||||
sendBotMessage('Buy/Sell Count: ' + str(totalBuyCount) +'/'+ str(totalSellCount));
|
||||
else:
|
||||
pass
|
||||
#策略逻辑
|
||||
onTick()
|
||||
#检测账户信息
|
||||
if tickCount % 15 == 0:
|
||||
updateAccount()
|
||||
if tickCount > 0:
|
||||
print ('Tick time: ', str((time.time() - startTime)/tickCount))
|
||||
#发送电报消息
|
||||
if tickCount % 100 == 0:
|
||||
sendBotMessage('Balance: '+str(currentBalance)+"\nPosition: "+str(currentPosition['size'])+"@"+str(currentPosition['entryPrice'])+"\nLiquidatePrice: "+str(currentPosition['liquidatePrice']) +"\nProfit: " + str(currentBalance - initBalance))
|
||||
#检测交易信息
|
||||
if tickCount % 15 == 0:
|
||||
lastTradeList = getTradeList(40)
|
||||
for index in range(0,len(lastTradeList) - 1):
|
||||
if tradeList[0]['id'] == lastTradeList[index]['id']:
|
||||
break
|
||||
if index == 0:
|
||||
pass
|
||||
else:
|
||||
# 有新交易记录
|
||||
tradeList = lastTradeList[:index] + tradeList
|
||||
for trade in lastTradeList[:index]:
|
||||
logger.info('NEW TRADE: %s,%s,%s',trade['side'],trade['price'],trade['size'])
|
||||
sendBotMessage('NEW TRADE - ' + str(trade['side']) + ' ' + str(trade['size']) + '@' + str(trade['price']))
|
||||
if trade['side'] == 'BUY':
|
||||
totalBuyCount += 1
|
||||
totalBuyPosition += trade['price'] * trade['size']
|
||||
totalBuyAmount += trade['size']
|
||||
elif trade['side'] == 'SELL':
|
||||
totalSellCount += 1
|
||||
totalSellPosition += trade['price'] * trade['size']
|
||||
totalSellAmount += trade['size']
|
||||
if totalBuyAmount != 0:
|
||||
print ('Buy position at', totalBuyPosition/totalBuyAmount, ' | ',totalBuyAmount)
|
||||
if totalSellAmount !=0:
|
||||
print ('Sell position at', totalSellPosition/totalSellAmount, ' | ',totalSellAmount)
|
||||
print ('Buy|Sell Count: ', totalBuyCount,'|',totalSellCount)
|
||||
logger.info('Buy|Sell Count: %s|%s',totalBuyCount,totalSellCount);
|
||||
tickCount += 1
|
||||
time.sleep(3)
|
||||
```
|
||||
|
||||
> 策略出处
|
||||
|
||||
https://www.fmz.com/strategy/162413
|
||||
|
||||
> 更新时间
|
||||
|
||||
2019-08-16 15:53:47
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
|
||||
> 策略名称
|
||||
|
||||
均线策略范例02
|
||||
|
||||
> 策略作者
|
||||
|
||||
小小梦
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
> 源码 (麦语言)
|
||||
|
||||
``` pascal
|
||||
M5H^^MA(H,5);
|
||||
M5L^^MA(L,5);
|
||||
C>M5H,BPK;
|
||||
C<M5L,SPK;
|
||||
C<MA(C,5) AND BKVOL>0,SP;
|
||||
C>MA(C,5) AND SKVOL>0,BP;
|
||||
AUTOFILTER;
|
||||
```
|
||||
|
||||
> 策略出处
|
||||
|
||||
https://www.fmz.com/strategy/156699
|
||||
|
||||
> 更新时间
|
||||
|
||||
2019-07-12 10:23:45
|
||||
@@ -0,0 +1,256 @@
|
||||
|
||||
> 策略名称
|
||||
|
||||
基于期货基本面分析的交易策略
|
||||
|
||||
> 策略作者
|
||||
|
||||
Hukybo
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
> 源码 (python)
|
||||
|
||||
``` python
|
||||
'''backtest
|
||||
start: 2019-01-01 00:00:00
|
||||
end: 2019-08-09 00:00:00
|
||||
period: 1d
|
||||
exchanges: [{"eid":"Futures_CTP","currency":"FUTURES"}]
|
||||
'''
|
||||
|
||||
|
||||
import requests
|
||||
import re
|
||||
from bs4 import BeautifulSoup
|
||||
import time
|
||||
import datetime
|
||||
import random
|
||||
import json
|
||||
import threading
|
||||
|
||||
|
||||
# 请求头文件
|
||||
request_headers = {
|
||||
'Connection': 'keep-alive',
|
||||
'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3',
|
||||
'accept-language': 'zh-CN,zh;q=0.9,en;q=0.8',
|
||||
'accept-encoding': 'gzip, deflate',
|
||||
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36'
|
||||
}
|
||||
|
||||
|
||||
# ip代理
|
||||
proxies = {
|
||||
'http': 'http://182.35.82.128:9999', 'https': 'https://182.35.82.128:9999'
|
||||
}
|
||||
|
||||
|
||||
diff_data = 0
|
||||
reserve_data = 0
|
||||
receipt_data = 0
|
||||
profit_data = 0
|
||||
|
||||
# 日期转时间戳
|
||||
def to_timestamp(date_str):
|
||||
times = date_str + " 00:00:00"
|
||||
time_array = time.strptime(times, "%Y-%m-%d %H:%M:%S")
|
||||
return int(round(time.mktime(time_array) * 1000))
|
||||
|
||||
|
||||
# 返回日期数组
|
||||
def date_arr(year, month, day):
|
||||
begin = datetime.date(year, month, day)
|
||||
end = datetime.date.today()
|
||||
arr = []
|
||||
for i in range((end - begin).days + 1):
|
||||
day = begin + datetime.timedelta(days=i)
|
||||
arr.append([str(day).replace('-', ''), str(day), day.weekday() + 1, to_timestamp(str(day))])
|
||||
return arr
|
||||
|
||||
|
||||
# 获取基差
|
||||
def spot_futures_diff_data(date, futures_name):
|
||||
global diff_data
|
||||
url = f"http://www.100ppi.com/sf2/day-{date}.html"
|
||||
try:
|
||||
url_text = requests.get(url, headers=request_headers).text # , proxies=proxies
|
||||
except BaseException:
|
||||
print('获取基差数据失败')
|
||||
return int(diff_data)
|
||||
soup = BeautifulSoup(url_text, "html5lib")
|
||||
results = soup.select("#fdata")[0]
|
||||
for i in results.find_all('tr'):
|
||||
if len(i.find_all('td', text=futures_name)) > 0:
|
||||
data = i.find_all('font')[0].text
|
||||
if data is not None:
|
||||
diff_data = data
|
||||
return int(diff_data)
|
||||
|
||||
|
||||
# 获取库存
|
||||
def spot_reserve_data(date, futures_name):
|
||||
global reserve_data
|
||||
url = f'http://www.shfe.com.cn/data/dailydata/{date}weeklystock.dat'
|
||||
try:
|
||||
url_text = requests.get(url, headers=request_headers).text
|
||||
except BaseException:
|
||||
print('获取库存数据失败')
|
||||
return reserve_data
|
||||
total = 0
|
||||
count = 0
|
||||
if url_text[0] == '{':
|
||||
for i in json.loads(url_text)['o_cursor']:
|
||||
if futures_name in i['VARNAME']:
|
||||
if '合计' not in i['WHABBRNAME'] and '总计' not in i['WHABBRNAME']:
|
||||
try:
|
||||
inventory = int(i['WHSTOCKS'])
|
||||
except BaseException:
|
||||
return reserve_data
|
||||
if inventory > 0:
|
||||
total = total + inventory
|
||||
count = count + 1
|
||||
if count > 0:
|
||||
reserve_data = int(total / count)
|
||||
return reserve_data
|
||||
|
||||
|
||||
# 获取仓单
|
||||
def spot_receipt_data(date, futures_name):
|
||||
global receipt_data
|
||||
url = f'http://www.shfe.com.cn/data/dailydata/{date}dailystock.dat'
|
||||
try:
|
||||
url_text = requests.get(url, headers=request_headers).text
|
||||
except BaseException:
|
||||
print('获取库存数据失败')
|
||||
return receipt_data
|
||||
total = 0
|
||||
count = 0
|
||||
if url_text[0] == '{':
|
||||
for i in json.loads(url_text)['o_cursor']:
|
||||
if futures_name in i['VARNAME']:
|
||||
if '合计' not in i['WHABBRNAME'] and '总计' not in i['WHABBRNAME']:
|
||||
try:
|
||||
inventory = int(i['WRTWGHTS'])
|
||||
except BaseException:
|
||||
return receipt_data
|
||||
if inventory > 0:
|
||||
total = total + inventory
|
||||
count = count + 1
|
||||
if count > 0:
|
||||
receipt_data = int(total / count)
|
||||
return receipt_data
|
||||
|
||||
|
||||
# 虚拟利润
|
||||
def get_profit_data():
|
||||
global profit_data
|
||||
profit_data = profit_data + random.randint(-100, 150)
|
||||
return profit_data
|
||||
|
||||
|
||||
def main():
|
||||
# threading.Thread(target=spot_futures_diff_arr, args=('天然橡胶',)).start()
|
||||
# threading.Thread(target=commodity_inventory_arr, args=('天然橡胶',)).start()
|
||||
# threading.Thread(target=commodity_warehouse_receipt_arr, args=('天然橡胶',)).start()
|
||||
|
||||
cfgA = {
|
||||
"extension": {
|
||||
"layout": 'single',
|
||||
"col": 6,
|
||||
"height": "500px",
|
||||
},
|
||||
"title" : {
|
||||
"text" : "基差图表"
|
||||
},
|
||||
"xAxis" : {
|
||||
"type" : "datetime"
|
||||
},
|
||||
"series" : [{
|
||||
"name" : "基差",
|
||||
"data" : [],
|
||||
}]
|
||||
}
|
||||
cfgB = {
|
||||
"extension": {
|
||||
"layout": 'single',
|
||||
"col": 6,
|
||||
"height": "500px",
|
||||
},
|
||||
"title" : {
|
||||
"text" : "库存图表"
|
||||
},
|
||||
"xAxis" : {
|
||||
"type" : "datetime"
|
||||
},
|
||||
"series" : [{
|
||||
"name" : "库存",
|
||||
"data" : [],
|
||||
}]
|
||||
}
|
||||
cfgC = {
|
||||
"extension": {
|
||||
"layout": 'single',
|
||||
"col": 6,
|
||||
"height": "500px",
|
||||
},
|
||||
"title" : {
|
||||
"text" : "仓单图表"
|
||||
},
|
||||
"xAxis" : {
|
||||
"type" : "datetime"
|
||||
},
|
||||
"series" : [{
|
||||
"name" : "仓单",
|
||||
"data" : [],
|
||||
}]
|
||||
}
|
||||
cfgD = {
|
||||
"extension": {
|
||||
"layout": 'single',
|
||||
"col": 6,
|
||||
"height": "500px",
|
||||
},
|
||||
"title" : {
|
||||
"text" : "利润图表"
|
||||
},
|
||||
"xAxis" : {
|
||||
"type" : "datetime"
|
||||
},
|
||||
"series" : [{
|
||||
"name" : "利润",
|
||||
"data" : [],
|
||||
}]
|
||||
}
|
||||
LogReset()
|
||||
chart = Chart([cfgA, cfgB, cfgC, cfgD])
|
||||
chart.reset()
|
||||
for i in date_arr(2018, 1, 1):
|
||||
diff = spot_futures_diff_data(i[1], '天然橡胶')
|
||||
reserve = spot_reserve_data(i[0], '天然橡胶')
|
||||
receipt = spot_receipt_data(i[0], '天然橡胶')
|
||||
profit = get_profit_data()
|
||||
if diff != 0 and reserve != 0 and receipt != 0:
|
||||
chart.add(0, [i[3], diff])
|
||||
chart.add(1, [i[3], reserve])
|
||||
chart.add(2, [i[3], receipt])
|
||||
chart.add(3, [i[3], profit])
|
||||
chart.update([cfgA, cfgB, cfgC, cfgD])
|
||||
time.sleep(1)
|
||||
|
||||
Log(f'基差:{diff} 库存:{reserve} 仓单:{receipt} 日期:{i[1]}')
|
||||
|
||||
|
||||
|
||||
|
||||
```
|
||||
|
||||
> 策略出处
|
||||
|
||||
https://www.fmz.com/strategy/161412
|
||||
|
||||
> 更新时间
|
||||
|
||||
2019-08-17 11:40:22
|
||||
@@ -0,0 +1,54 @@
|
||||
|
||||
> 策略名称
|
||||
|
||||
基于箱体理论的交易策略,支持商品期货和数字货币
|
||||
|
||||
> 策略作者
|
||||
|
||||
Hukybo
|
||||
|
||||
> 策略描述
|
||||
|
||||
**策略简介**
|
||||
所谓的箱体,也可以称为一种形态,它的理论根基借鉴了支撑线和阻力线。通常情况下,当价格上涨到前期高点时,会遇到卖压使价格回落,当价格下跌到前期低点时,会遇到买压使价格上升。如果价格多次这样来来回回上下运动,就可以根据一段时间的历史价格的高点与低点,形成一个箱体(Box theory)。
|
||||
|
||||
[点击阅读更多内容](https://www.fmz.com/bbs-topic/4068)
|
||||
|
||||
> 策略参数
|
||||
|
||||
|
||||
|
||||
|参数|默认值|描述|
|
||||
|----|----|----|
|
||||
|N|25|N|
|
||||
|
||||
|
||||
> 源码 (麦语言)
|
||||
|
||||
``` pascal
|
||||
(*backtest
|
||||
start: 2015-02-22 00:00:00
|
||||
end: 2019-07-20 00:00:00
|
||||
period: 1h
|
||||
exchanges: [{"eid":"Futures_CTP","currency":"FUTURES"}]
|
||||
args: [["SlideTick",0,126961],["ContractType","i888",126961]]
|
||||
*)
|
||||
|
||||
PRICE:=(OPEN+HIGH+LOW+CLOSE*2)/5;
|
||||
UPPERBAND^^REF(HHV(PRICE,N),1);
|
||||
LOWERBAND^^REF(LLV(PRICE,N),1);
|
||||
C>=UPPERBAND,BPK;
|
||||
C<=LOWERBAND,SPK;
|
||||
MID^^(UPPERBAND+LOWERBAND)/2;
|
||||
C<MID||C<LLV(PRICE,N/2),SP;
|
||||
C>MID||C>HHV(PRICE,N/2),BP;
|
||||
AUTOFILTER;
|
||||
```
|
||||
|
||||
> 策略出处
|
||||
|
||||
https://www.fmz.com/strategy/158088
|
||||
|
||||
> 更新时间
|
||||
|
||||
2019-07-20 16:26:14
|
||||
+114
@@ -0,0 +1,114 @@
|
||||
|
||||
> 策略名称
|
||||
|
||||
多线程批量任务
|
||||
|
||||
> 策略作者
|
||||
|
||||
jason47
|
||||
|
||||
> 策略描述
|
||||
|
||||
```
|
||||
var tasks = $.go([
|
||||
[exchange, 'GetDepth'],
|
||||
[exchange, 'GetTicker'],
|
||||
])
|
||||
var retList = $.wait(tasks)
|
||||
Log(retList[0])
|
||||
Log(retList[1])
|
||||
```
|
||||
|
||||
- **$.go()**
|
||||
> 批量执行Go
|
||||
> 参数类型 二维数组
|
||||
```
|
||||
[
|
||||
[exchange, 'GetDepth'],
|
||||
[exchange, 'GetTicker'],
|
||||
]
|
||||
```
|
||||
> 返回值 cmdList 数组
|
||||
|
||||
- **$.wait(cmdList)**
|
||||
> 获取结果
|
||||
|
||||
- **$.gowait()**
|
||||
> 执行Go并且获取结果
|
||||
|
||||
> 策略参数
|
||||
|
||||
|
||||
|
||||
|参数|默认值|描述|
|
||||
|----|----|----|
|
||||
|RETRY_INTERVAL|true|重试间隔(秒)|
|
||||
|
||||
|
||||
> 源码 (javascript)
|
||||
|
||||
``` javascript
|
||||
$.go = function(tasks) {
|
||||
return _.map(tasks, function(args) {
|
||||
return {
|
||||
result: null,
|
||||
args: args,
|
||||
defer: args[0].Go.apply(args[0], args.slice(1))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
$.gowait = function(tasks, maxConcurrent, retry) {
|
||||
return $.wait($.go(tasks), maxConcurrent, retry)
|
||||
}
|
||||
|
||||
$.wait = function(cmdList, maxConcurrent, retry) {
|
||||
retry = retry || true
|
||||
maxConcurrent = Math.min(cmdList.length, maxConcurrent || 5)
|
||||
var total = Math.ceil(cmdList.length / maxConcurrent)
|
||||
return _.reduce(_.range(total), function(beforeResultList, currentPage) {
|
||||
var lastCmdList = cmdList.slice(currentPage * maxConcurrent, currentPage * maxConcurrent + maxConcurrent)
|
||||
while (1) {
|
||||
lastCmdList = _.map(lastCmdList, function(cmd) {
|
||||
var args = cmd.args
|
||||
if (cmd.result == null) {
|
||||
var ret = cmd.defer.wait()
|
||||
if (ret != null) {
|
||||
return _.extend(cmd, {
|
||||
result: ret
|
||||
})
|
||||
} else if (retry === true) {
|
||||
return _.extend(cmd, {
|
||||
defer: args[0].Go.apply(args[0], args.slice(1)),
|
||||
result: null
|
||||
})
|
||||
}
|
||||
}
|
||||
return cmd
|
||||
})
|
||||
var isFinished = _.every(lastCmdList, function(cmd) {
|
||||
return cmd.result
|
||||
})
|
||||
if (isFinished) {
|
||||
break
|
||||
}
|
||||
Sleep(RETRY_INTERVAL * 1000)
|
||||
}
|
||||
var newResultList = lastCmdList.map(function(cmd) {
|
||||
return cmd.result
|
||||
})
|
||||
if (currentPage + 1 < total) {
|
||||
Sleep(500)
|
||||
}
|
||||
return beforeResultList.concat(newResultList)
|
||||
}, [])
|
||||
}
|
||||
```
|
||||
|
||||
> 策略出处
|
||||
|
||||
https://www.fmz.com/strategy/159978
|
||||
|
||||
> 更新时间
|
||||
|
||||
2019-08-24 04:51:11
|
||||
@@ -0,0 +1,164 @@
|
||||
|
||||
> 策略名称
|
||||
|
||||
时间格式工具
|
||||
|
||||
> 策略作者
|
||||
|
||||
huangsibo
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
> 源码 (javascript)
|
||||
|
||||
``` javascript
|
||||
/*
|
||||
* @Author: sibohuang
|
||||
* @Date: 2019-08-10 09:36:03
|
||||
* @LastEditors: sibohuang
|
||||
* @LastEditTime: 2019-08-10 09:45:11
|
||||
* @Description:
|
||||
* @Email: 3570411064@qq.com
|
||||
* @Company: igola
|
||||
*/
|
||||
|
||||
/**
|
||||
* 将日期格式化成指定格式的字符串
|
||||
* @param date 要格式化的日期,不传时默认当前时间,也可以是一个时间戳
|
||||
* @param fmt 目标字符串格式,支持的字符有:y,M,d,q,w,H,h,m,S,默认:yyyy-MM-dd HH:mm:ss
|
||||
* @returns 返回格式化后的日期字符串
|
||||
*/
|
||||
$.formatDate = function (date, fmt) {
|
||||
date = !date ? new Date() : date;
|
||||
date = typeof date === 'number' ? new Date(date) : date;
|
||||
fmt = fmt || 'yyyy-MM-dd HH:mm:ss';
|
||||
var obj =
|
||||
{
|
||||
'y': date.getFullYear(), // 年份,注意必须用getFullYear
|
||||
'M': date.getMonth() + 1, // 月份,注意是从0-11
|
||||
'd': date.getDate(), // 日期
|
||||
'q': Math.floor((date.getMonth() + 3) / 3), // 季度
|
||||
'w': date.getDay(), // 星期,注意是0-6
|
||||
'H': date.getHours(), // 24小时制
|
||||
'h': date.getHours() % 12 == 0 ? 12 : date.getHours() % 12, // 12小时制
|
||||
'm': date.getMinutes(), // 分钟
|
||||
's': date.getSeconds(), // 秒
|
||||
'S': date.getMilliseconds() // 毫秒
|
||||
};
|
||||
var week = ['天', '一', '二', '三', '四', '五', '六'];
|
||||
for (var i in obj) {
|
||||
fmt = fmt.replace(new RegExp(i + '+', 'g'), function (m) {
|
||||
var val = obj[i] + '';
|
||||
if (i == 'w') return (m.length > 2 ? '星期' : '周') + week[val];
|
||||
for (var j = 0, len = val.length; j < m.length - len; j++) val = '0' + val;
|
||||
return m.length == 1 ? val : val.substring(val.length - m.length);
|
||||
});
|
||||
}
|
||||
return fmt;
|
||||
}
|
||||
/**
|
||||
* 将字符串解析成日期
|
||||
* @param str 输入的日期字符串,如'2014-09-13'
|
||||
* @param fmt 字符串格式,默认'yyyy-MM-dd',支持如下:y、M、d、H、m、s、S,不支持w和q
|
||||
* @returns 解析后的Date类型日期
|
||||
*/
|
||||
|
||||
$.parseDate = function(str, fmt) {
|
||||
fmt = fmt || 'yyyy-MM-dd';
|
||||
var obj = {y: 0, M: 1, d: 0, H: 0, h: 0, m: 0, s: 0, S: 0};
|
||||
fmt.replace(/([^yMdHmsS]*?)(([yMdHmsS])\3*)([^yMdHmsS]*?)/g, function (m, $1, $2, $3, $4, idx, old) {
|
||||
str = str.replace(new RegExp($1 + '(\\d{' + $2.length + '})' + $4), function (_m, _$1) {
|
||||
obj[$3] = parseInt(_$1);
|
||||
return '';
|
||||
});
|
||||
return '';
|
||||
});
|
||||
obj.M--; // 月份是从0开始的,所以要减去1
|
||||
var date = new Date(obj.y, obj.M, obj.d, obj.H, obj.m, obj.s);
|
||||
if (obj.S !== 0) date.setMilliseconds(obj.S); // 如果设置了毫秒
|
||||
return date;
|
||||
}
|
||||
/**
|
||||
* 将一个日期格式化成友好格式,比如,1分钟以内的返回“刚刚”,
|
||||
* 当天的返回时分,当年的返回月日,否则,返回年月日
|
||||
* @param {Object} date
|
||||
*/
|
||||
$.formatDateToFriendly = function(date) {
|
||||
date = date || new Date();
|
||||
date = typeof date === 'number' ? new Date(date) : date;
|
||||
invariant(date instanceof Date, 'date is not date type');
|
||||
var now = new Date();
|
||||
if ((now.getTime() - date.getTime()) < 60 * 1000) return '刚刚'; // 1分钟以内视作“刚刚”
|
||||
var temp = this.formatDate(date, 'yyyy年M月d');
|
||||
if (temp == this.formatDate(now, 'yyyy年M月d')) return this.formatDate(date, 'HH:mm');
|
||||
if (date.getFullYear() == now.getFullYear()) return this.formatDate(date, 'M月d日');
|
||||
return temp;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将一段时长转换成友好格式,如:
|
||||
* 147->“2分27秒”
|
||||
* 1581->“26分21秒”
|
||||
* 15818->“4小时24分”
|
||||
* @param {Object} second
|
||||
*/
|
||||
|
||||
$.formatDurationToFriendly = function(second) {
|
||||
if (second < 60) return second + '秒';
|
||||
else if (second < 60 * 60) return (second - second % 60) / 60 + '分' + second % 60 + '秒';
|
||||
else if (second < 60 * 60 * 24) return (second - second % 3600) / 60 / 60 + '小时' + Math.round(second % 3600 / 60) + '分';
|
||||
return (second / 60 / 60 / 24).toFixed(1) + '天';
|
||||
}
|
||||
/**
|
||||
* 将时间转换成MM:SS形式
|
||||
*/
|
||||
|
||||
$.formatTimeToFriendly = function(second) {
|
||||
var m = Math.floor(second / 60);
|
||||
m = m < 10 ? ( '0' + m ) : m;
|
||||
var s = second % 60;
|
||||
s = s < 10 ? ( '0' + s ) : s;
|
||||
return m + ':' + s;
|
||||
}
|
||||
/**
|
||||
* 计算2日期之间的天数,用的是比较毫秒数的方法
|
||||
* 传进来的日期要么是Date类型,要么是yyyy-MM-dd格式的字符串日期
|
||||
* @param date1 日期一
|
||||
* @param date2 日期二
|
||||
*/
|
||||
$.countDays = function(date1, date2) {
|
||||
var fmt = 'yyyy-MM-dd';
|
||||
// 将日期转换成字符串,转换的目的是去除“时、分、秒”
|
||||
if (date1 instanceof Date && date2 instanceof Date) {
|
||||
date1 = this.formatDate(date1, fmt);
|
||||
date2 = this.formatDate(date2, fmt);
|
||||
}
|
||||
if (typeof date1 === 'string' && typeof date2 === 'string') {
|
||||
date1 = this.parseDate(date1, fmt);
|
||||
date2 = this.parseDate(date2, fmt);
|
||||
return (date1.getTime() - date2.getTime()) / (1000 * 60 * 60 * 24);
|
||||
}
|
||||
else {
|
||||
console.error('参数格式无效!');
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
function main() {
|
||||
Log($.formatDate(new Date()))
|
||||
// Log($.parseDate(new Date()))
|
||||
// Log($.formatDateToFriendly(new Date()))
|
||||
// Log($.formatTimeToFriendly(new Date()))
|
||||
// Log($.formatDurationToFriendly(new Date()))
|
||||
// Log($.countDays(new Date()))
|
||||
}
|
||||
```
|
||||
|
||||
> 策略出处
|
||||
|
||||
https://www.fmz.com/strategy/161505
|
||||
|
||||
> 更新时间
|
||||
|
||||
2019-08-10 09:45:12
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
|
||||
> 策略名称
|
||||
|
||||
期货看穿式测试
|
||||
|
||||
> 策略作者
|
||||
|
||||
Hukybo
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
> 源码 (javascript)
|
||||
|
||||
``` javascript
|
||||
num = 0
|
||||
|
||||
function onTick() {
|
||||
if (num < 1) {
|
||||
_C(exchange.SetContractType, "rb1910")
|
||||
account = _C(exchange.GetAccount).Info.AccountID
|
||||
Log("账号:", account);
|
||||
Log("AppID:", "client_fmz_1.0");
|
||||
records = _C(exchange.GetRecords)
|
||||
k = records[records.length - 1].Close
|
||||
exchange.SetDirection("buy");
|
||||
id = exchange.Buy(k + 2, 1)
|
||||
Log("sessionid:", id);
|
||||
Log("登陆时间:", Date());
|
||||
|
||||
Log("账号:", account);
|
||||
records = _C(exchange.GetRecords)
|
||||
k = records[records.length - 1].Close
|
||||
for (x = 0; x < 5; x++) {
|
||||
exchange.SetDirection("buy");
|
||||
id = exchange.Buy(k + 2, 1)
|
||||
Log("sessionid:", id);
|
||||
Sleep(1000)
|
||||
}
|
||||
|
||||
Log("账号:", account);
|
||||
for (y = 0; y < 5; y++) {
|
||||
exchange.SetDirection("buy");
|
||||
id = exchange.Buy(k - 2, 1)
|
||||
Log("sessionid:", id);
|
||||
Sleep(1000)
|
||||
}
|
||||
orders = _C(exchange.GetOrders)
|
||||
for (z = 0; z < 5; z++) {
|
||||
Log("账号:", account);
|
||||
Log("合约:", "rb1910");
|
||||
var ordersid = orders[z].Id
|
||||
if (exchange.CancelOrder(ordersid)) {
|
||||
Log("订单:" + ordersid + " 已撤单")
|
||||
}
|
||||
}
|
||||
num++
|
||||
}
|
||||
}
|
||||
|
||||
function main() {
|
||||
while (true) {
|
||||
onTick();
|
||||
Sleep(1000);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
> 策略出处
|
||||
|
||||
https://www.fmz.com/strategy/152670
|
||||
|
||||
> 更新时间
|
||||
|
||||
2019-07-29 10:30:46
|
||||
@@ -0,0 +1,183 @@
|
||||
|
||||
> 策略名称
|
||||
|
||||
测试图表
|
||||
|
||||
> 策略作者
|
||||
|
||||
Hukybo
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
> 源码 (python)
|
||||
|
||||
``` python
|
||||
'''backtest
|
||||
start: 2019-01-01 00:00:00
|
||||
end: 2019-08-09 00:00:00
|
||||
period: 1d
|
||||
exchanges: [{"eid":"Futures_CTP","currency":"FUTURES"}]
|
||||
'''
|
||||
|
||||
|
||||
import requests
|
||||
import re
|
||||
from bs4 import BeautifulSoup
|
||||
import time
|
||||
import datetime
|
||||
import random
|
||||
import json
|
||||
import threading
|
||||
|
||||
|
||||
# 请求头文件
|
||||
request_headers = {
|
||||
'Connection': 'keep-alive',
|
||||
'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3',
|
||||
'accept-language': 'zh-CN,zh;q=0.9,en;q=0.8',
|
||||
'accept-encoding': 'gzip, deflate',
|
||||
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36'
|
||||
}
|
||||
|
||||
|
||||
# ip代理
|
||||
proxies = {
|
||||
'http': 'http://182.35.82.128:9999', 'https': 'https://182.35.82.128:9999'
|
||||
}
|
||||
|
||||
|
||||
diff_old_data = 0
|
||||
|
||||
|
||||
# 日期转时间戳
|
||||
def to_timestamp(date_str):
|
||||
times = date_str + " 00:00:00"
|
||||
time_array = time.strptime(times, "%Y-%m-%d %H:%M:%S")
|
||||
return int(round(time.mktime(time_array)))
|
||||
|
||||
|
||||
# 返回日期数组
|
||||
def date_arr(year, month, day):
|
||||
begin = datetime.date(year, month, day)
|
||||
end = datetime.date.today()
|
||||
arr = []
|
||||
for i in range((end - begin).days + 1):
|
||||
day = begin + datetime.timedelta(days=i)
|
||||
arr.append([str(day).replace('-', ''), str(day), day.weekday() + 1, to_timestamp(str(day))])
|
||||
return arr
|
||||
|
||||
|
||||
def spot_futures_diff_data(date, futures_name):
|
||||
global diff_old_data
|
||||
url = f"http://www.100ppi.com/sf2/day-{date}.html"
|
||||
try:
|
||||
url_text = requests.get(url, headers=request_headers).text # , proxies=proxies
|
||||
except BaseException:
|
||||
print('由于连接方在一段时间后没有正确答复或连接的主机没有反应,连接尝试失败。')
|
||||
return int(diff_old_data)
|
||||
soup = BeautifulSoup(url_text, "html5lib")
|
||||
results = soup.select("#fdata")[0]
|
||||
for i in results.find_all('tr'):
|
||||
if len(i.find_all('td', text=futures_name)) > 0:
|
||||
data = i.find_all('font')[0].text
|
||||
if data is not None:
|
||||
diff_old_data = data
|
||||
return int(data)
|
||||
return int(diff_old_data)
|
||||
|
||||
|
||||
def main():
|
||||
# threading.Thread(target=spot_futures_diff_arr, args=('天然橡胶',)).start()
|
||||
# threading.Thread(target=commodity_inventory_arr, args=('天然橡胶',)).start()
|
||||
# threading.Thread(target=commodity_warehouse_receipt_arr, args=('天然橡胶',)).start()
|
||||
|
||||
cfgA = {
|
||||
"extension": {
|
||||
"layout": 'single',
|
||||
"col": 6,
|
||||
"height": "500px",
|
||||
},
|
||||
"title" : {
|
||||
"text" : "基差图表"
|
||||
},
|
||||
"xAxis" : {
|
||||
"type" : "datetime"
|
||||
},
|
||||
"series" : [{
|
||||
"name" : "基差",
|
||||
"data" : [],
|
||||
}]
|
||||
}
|
||||
cfgB = {
|
||||
"extension": {
|
||||
"layout": 'single',
|
||||
"col": 6,
|
||||
"height": "500px",
|
||||
},
|
||||
"title" : {
|
||||
"text" : "库存图表"
|
||||
},
|
||||
"xAxis" : {
|
||||
"type" : "datetime"
|
||||
},
|
||||
"series" : [{
|
||||
"name" : "库存",
|
||||
"data" : [],
|
||||
}]
|
||||
}
|
||||
cfgC = {
|
||||
"extension": {
|
||||
"layout": 'single',
|
||||
"col": 6,
|
||||
"height": "500px",
|
||||
},
|
||||
"title" : {
|
||||
"text" : "仓单图表"
|
||||
},
|
||||
"xAxis" : {
|
||||
"type" : "datetime"
|
||||
},
|
||||
"series" : [{
|
||||
"name" : "仓单",
|
||||
"data" : [],
|
||||
}]
|
||||
}
|
||||
cfgD = {
|
||||
"extension": {
|
||||
"layout": 'single',
|
||||
"col": 6,
|
||||
"height": "500px",
|
||||
},
|
||||
"title" : {
|
||||
"text" : "利润图表"
|
||||
},
|
||||
"xAxis" : {
|
||||
"type" : "datetime"
|
||||
},
|
||||
"series" : [{
|
||||
"name" : "利润",
|
||||
"data" : [],
|
||||
}]
|
||||
}
|
||||
chart = Chart([cfgA, cfgB, cfgC, cfgD])
|
||||
for i in date_arr(2018, 1, 1):
|
||||
diff = spot_futures_diff_data(i[1], '天然橡胶')
|
||||
chart.add(0, [i[3], diff])
|
||||
chart.add(1, [i[3], diff])
|
||||
chart.add(2, [i[3], diff])
|
||||
chart.add(3, [i[3], diff])
|
||||
Log('时间戳', i[3], type(i[3]))
|
||||
Log('数据', diff, type(diff))
|
||||
#chart.update([cfgA, cfgB, cfgC, cfgD])
|
||||
time.sleep(1)
|
||||
```
|
||||
|
||||
> 策略出处
|
||||
|
||||
https://www.fmz.com/strategy/162093
|
||||
|
||||
> 更新时间
|
||||
|
||||
2019-08-15 16:55:35
|
||||
@@ -0,0 +1,49 @@
|
||||
|
||||
> 策略名称
|
||||
|
||||
火币点卡
|
||||
|
||||
> 策略作者
|
||||
|
||||
jason47
|
||||
|
||||
> 策略描述
|
||||
|
||||
- getHBPoints(exchange)
|
||||
> 获取火币点卡
|
||||
> 返回值,number类型
|
||||
|
||||
|
||||
|
||||
> 源码 (javascript)
|
||||
|
||||
``` javascript
|
||||
$.getHBPoints = function(ex) {
|
||||
try {
|
||||
var accountRet = ex.IO("api", "GET", "/v1/account/accounts")
|
||||
if (accountRet && accountRet.data) {
|
||||
var account = _.findWhere(accountRet.data, {
|
||||
type: 'point'
|
||||
})
|
||||
if (account && account.id) {
|
||||
var balanceRet = ex.IO("api", "GET", "/v1/account/accounts/" + account.id + "/balance")
|
||||
if (balanceRet && balanceRet.data && balanceRet.data.list) {
|
||||
var balance = _.reduce(balanceRet.data.list, function(p, n) {
|
||||
return new Decimal(p).plus(n.balance).toNumber()
|
||||
}, 0)
|
||||
return balance
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e) {}
|
||||
return null
|
||||
}
|
||||
```
|
||||
|
||||
> 策略出处
|
||||
|
||||
https://www.fmz.com/strategy/162055
|
||||
|
||||
> 更新时间
|
||||
|
||||
2019-08-13 22:05:07
|
||||
@@ -0,0 +1,160 @@
|
||||
|
||||
> 策略名称
|
||||
|
||||
火币精度
|
||||
|
||||
> 策略作者
|
||||
|
||||
jason47
|
||||
|
||||
> 策略描述
|
||||
|
||||
- **$.getAmountPrecision(Currency)**
|
||||
> 交易对基础币种计数精度(小数点后位数)
|
||||
> 返回值 number类型
|
||||
|
||||
- **$.getPricePrecision(Currency)**
|
||||
> 交易对报价的精度(小数点后位数)
|
||||
> 返回值 number类型
|
||||
|
||||
- **$.getMinOrderAmount(Currency)**
|
||||
> 交易对最小下单量
|
||||
> 返回值 number类型
|
||||
|
||||
- **$.getMaxOrderAmount(Currency)**
|
||||
> 交易对最大下单量
|
||||
> 返回值 number类型
|
||||
|
||||
- **$.getValuePrecision(Currency)**
|
||||
> 交易对交易金额的精度(小数点后位数)
|
||||
> 返回值 number类型
|
||||
|
||||
- **$.getMinValue(Currency)**
|
||||
> 最小下单金额 (下单金额指当订单类型为限价单时,下单接口传入的(amount * price)。当订单类型为buy-market时,下单接口传的'amount')
|
||||
> 返回值 number类型
|
||||
|
||||
> 策略参数
|
||||
|
||||
|
||||
|
||||
|参数|默认值|描述|
|
||||
|----|----|----|
|
||||
|TICKER_EXPIRE_TIME|600|行情缓存时间(秒)|
|
||||
|
||||
|
||||
> 源码 (javascript)
|
||||
|
||||
``` javascript
|
||||
var _symbols = _C(getSymbols)
|
||||
var _cacheTicker = {}
|
||||
|
||||
function parseJson(str) {
|
||||
try {
|
||||
return JSON.parse(str)
|
||||
} catch(e) {
|
||||
Log(str + e.message + '#FF0000')
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
function getSymbols() {
|
||||
var ret = parseJson(HttpQuery('https://api.huobi.pro/v1/common/symbols'))
|
||||
if (ret && ret.status === 'ok') {
|
||||
return ret.data
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
function getTicker(ex) {
|
||||
var currency = getCurreny(ex)
|
||||
if (_cacheTicker[currency] && Unix() - _cacheTicker[currency].UpdatedAt < TICKER_EXPIRE_TIME) {
|
||||
return _cacheTicker[currency]
|
||||
}
|
||||
var sym = currency.replace('_', '').toLocaleLowerCase()
|
||||
var ret = parseJson(HttpQuery('https://api.huobi.pro/market/detail/merged?symbol=' + sym))
|
||||
if (ret && ret.status === 'ok') {
|
||||
_cacheTicker[currency] = _.extend({ UpdatedAt: Unix() }, ret.tick)
|
||||
return ret.data
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
function getCurreny(ex) {
|
||||
var currency = null
|
||||
if (typeof(ex) === 'object') {
|
||||
return ex.GetCurrency()
|
||||
} else {
|
||||
return ex
|
||||
}
|
||||
}
|
||||
|
||||
function getCoin(ex) {
|
||||
var currency = getCurreny(ex)
|
||||
var currencyAry = _.map(currency.split('_'), function(sym) { return sym.toLocaleLowerCase() })
|
||||
return _.find(_symbols, function(coin) {
|
||||
return currencyAry[0] === coin['base-currency'] && currencyAry[1] === coin['quote-currency']
|
||||
})
|
||||
}
|
||||
|
||||
$.getSymbols = getSymbols
|
||||
|
||||
$.getAmountPrecision = function (ex) {
|
||||
var coin = getCoin(ex)
|
||||
if (coin) {
|
||||
return coin['amount-precision']
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
$.getPricePrecision = function(ex) {
|
||||
var coin = getCoin(ex)
|
||||
if (coin) {
|
||||
return coin['price-precision']
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
$.getMinOrderAmount = function(ex, price) {
|
||||
var coin = getCoin(ex)
|
||||
if (coin) {
|
||||
price = price || _C(getTicker, ex).close
|
||||
return +(new Decimal(coin['min-order-value']).mul(1.1).div(price).toFixed($.getAmountPrecision(ex), Decimal.ROUND_CEIL))
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
$.getMaxOrderAmount = function(ex) {
|
||||
var coin = getCoin(ex)
|
||||
if (coin) {
|
||||
return coin['max-order-amt']
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
$.getValuePrecision = function(ex) {
|
||||
var coin = getCoin(ex)
|
||||
if (coin) {
|
||||
return coin['value-precision']
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
$.getMinValue = function(ex) {
|
||||
var coin = getCoin(ex)
|
||||
if (coin) {
|
||||
return coin['min-order-value']
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
> 策略出处
|
||||
|
||||
https://www.fmz.com/strategy/159104
|
||||
|
||||
> 更新时间
|
||||
|
||||
2019-07-31 18:15:19
|
||||
+400
@@ -0,0 +1,400 @@
|
||||
|
||||
> 策略名称
|
||||
|
||||
现现对冲(多交易所搬砖)
|
||||
|
||||
> 策略作者
|
||||
|
||||
vipjohn
|
||||
|
||||
> 策略描述
|
||||
|
||||
策略功能:
|
||||
实现跨交易所的现现对冲
|
||||
支持无限个交易所同时搬砖
|
||||
支持任意交易对搬砖
|
||||
详细报表支持(完整的状态信息, 可实时看到策略观察到的各个交易所的差价信息, 持仓信息,资金净值,策略运行状况)
|
||||
24小时无人值守, 全自动运行
|
||||
|
||||
策略参数设置:
|
||||
请访问 https://www.pcclean.io/9wbl
|
||||
|
||||
|
||||
|
||||
> 源码 (javascript)
|
||||
|
||||
``` javascript
|
||||
/*
|
||||
现现对冲(搬砖)
|
||||
|
||||
注意:
|
||||
1.确保设置了交易所的手续费和最小买入量
|
||||
|
||||
版本历史:
|
||||
21:33 2018/10/25 处理挂单不成交的情况,不再使用市价单
|
||||
7:28 2018/10/26 支持永久记录收益
|
||||
8:00 2018/10/27 支持显示币数变化,搬砖函数每次更新getaccount
|
||||
10:14 2018/10/27 修复不显示profit chart的问题
|
||||
*/
|
||||
|
||||
//搬砖参数设置
|
||||
var handfee= {FCoin:0.0 , CoinEx:0.0004,Huobi:0.002,OKEX:0.002,Binance:0.001,ZB:0.002 };//手续费
|
||||
var minestbuy={FCoin:0.01, CoinEx:0.01, Huobi:0.01, OKEX:0.01, Binance:0.04, ZB:0.01 }; //最小买入量
|
||||
var want_profit=0.0001;//期望收益
|
||||
var price_step=0.00001;//定价单调整价格的单位
|
||||
var maxbuy=5; //最大买入
|
||||
var warning_stocks=10;//余额预警
|
||||
var price_n=6; //价格精度
|
||||
var num_n=3; //数量精度
|
||||
var max_wait_order=15000;//订单等待时间
|
||||
var buypercent=1.0; //买入百分比
|
||||
var wait_ms=3000;//重试等待间隔时间
|
||||
var enable_traders_recorder=true;//记录所有交易
|
||||
|
||||
|
||||
//全局变量
|
||||
var limit_orders=[];//限价单
|
||||
var total_banzhuan=0;//搬砖次数
|
||||
var total_op=0;//总搬砖机会
|
||||
var exchange_banzhuan={};//记录交易所搬砖次数
|
||||
var total_passed=0;//因为余额不足错过了多少次搬砖机会
|
||||
var trades=[];//所有交易
|
||||
var approximate_profit=0;//盈亏近似值
|
||||
var dealamount_change=0;//币数变化
|
||||
|
||||
function get_exchange_banzhuan(exname){
|
||||
var name = exname.replace(" ", "_");
|
||||
if (isNaN(exchange_banzhuan[name])){
|
||||
return 0;
|
||||
}else{
|
||||
return exchange_banzhuan[name];
|
||||
}
|
||||
}
|
||||
function put_exchange_banzhuan(exname,value){
|
||||
var name = exname.replace(" ", "_");
|
||||
exchange_banzhuan[name]=value;
|
||||
}
|
||||
|
||||
//在交易所ex1和ex2之间搬砖
|
||||
function banzhuan(exchange1,exchange2)
|
||||
{
|
||||
//read price from exchange 1
|
||||
var account1=_C(exchange1.GetAccount);
|
||||
var exname1=exchange1.GetName();
|
||||
var depth1 = _C(exchange1.GetDepth);
|
||||
var askprice1=depth1.Asks[0].Price;
|
||||
var askamount1=depth1.Asks[0].Amount;
|
||||
|
||||
//read price from exchange 2
|
||||
var account2=_C(exchange2.GetAccount);
|
||||
var exname2=exchange2.GetName();
|
||||
var depth2 = _C(exchange2.GetDepth);
|
||||
var bidprice2=depth2.Bids[0].Price;
|
||||
var bidamount2=depth2.Bids[0].Amount;
|
||||
|
||||
var threshold=handfee[exname1]+handfee[exname2]+want_profit; //交易阀值
|
||||
var minbuy=Math.max(minestbuy[exname1],minestbuy[exname2]); //最小购买数量
|
||||
|
||||
//buy and sell if its prifitable
|
||||
var diff=(bidprice2-askprice1)/askprice1;
|
||||
var canbuy=Math.min(askamount1,bidamount2);
|
||||
canbuy=canbuy*buypercent;
|
||||
var min=Math.min(canbuy,maxbuy); //the minimum number for buy/sell
|
||||
|
||||
if (Math.abs(diff)>=threshold && min>=minbuy) //for bch
|
||||
{
|
||||
if (diff>0)//exchange 1 is cheap
|
||||
{
|
||||
total_op=total_op+2;
|
||||
put_exchange_banzhuan(exname1+"_op",get_exchange_banzhuan(exname1+"_op")+1);
|
||||
put_exchange_banzhuan(exname2+"_op",get_exchange_banzhuan(exname2+"_op")+1);
|
||||
put_exchange_banzhuan(exname1+"_canbuy",get_exchange_banzhuan(exname1+"_canbuy")+min);
|
||||
put_exchange_banzhuan(exname2+"_cansell",get_exchange_banzhuan(exname2+"_cansell")+min);
|
||||
|
||||
var needmoney=min*askprice1;
|
||||
if (account2.Stocks>min && account1.Balance>needmoney ){
|
||||
|
||||
//buy from exchange 1
|
||||
var price_1=askprice1;
|
||||
var buyID=retryBuy(exchange1,price_1,min);
|
||||
//sell from exchange 2
|
||||
var price_2=bidprice2;
|
||||
var sellID=retrySell(exchange2,price_2,min);
|
||||
|
||||
//add orders to pre-processing list
|
||||
var order1={
|
||||
exchange:exchange1,
|
||||
ID:buyID,
|
||||
create_time:new Date(),
|
||||
utcdate:get_ChinaTimeString()
|
||||
};
|
||||
limit_orders.push(order1);
|
||||
var order2={
|
||||
exchange:exchange2,
|
||||
ID:sellID,
|
||||
create_time:new Date(),
|
||||
utcdate:get_ChinaTimeString()
|
||||
};
|
||||
limit_orders.push(order2);
|
||||
|
||||
total_banzhuan=total_banzhuan+2;
|
||||
put_exchange_banzhuan(exname1,get_exchange_banzhuan(exname1)+1);
|
||||
put_exchange_banzhuan(exname2,get_exchange_banzhuan(exname2)+1);
|
||||
}else{
|
||||
if (account2.Stocks<=min){
|
||||
Log(exchange2.GetName()+"没币了,无法搬砖。请及时充值。"+"#ff0000");
|
||||
}
|
||||
if (account1.Balance<=needmoney){
|
||||
Log(exchange1.GetName()+"没钱了,无法搬砖。请及时充值。"+"#ff0000");
|
||||
}
|
||||
total_passed=total_passed+2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//检查订单是否执行成功,如果未完成,执行市价下单
|
||||
function process_limiteorders(){
|
||||
var cur_time=new Date();
|
||||
var limit_orders_new=[];
|
||||
for (var i=0; i<limit_orders.length; ++i){
|
||||
var create_time=limit_orders[i].create_time;
|
||||
var passedtime=cur_time-create_time;
|
||||
if (passedtime>max_wait_order){
|
||||
var exchange_c=limit_orders[i].exchange;
|
||||
var order_ID=limit_orders[i].ID;
|
||||
var exname=exchange_c.GetName();
|
||||
var account=_C(exchange_c.GetAccount);
|
||||
var ticker=_C(exchange_c.GetTicker);
|
||||
var last=ticker.Last;
|
||||
var orderdata=_C(exchange_c.GetOrder,order_ID);
|
||||
var type=orderdata.Type;
|
||||
|
||||
if (orderdata.Status!=ORDER_STATE_CLOSED){
|
||||
var notcompleted=orderdata.Amount-orderdata.DealAmount;
|
||||
exchange_c.CancelOrder(order_ID);
|
||||
if (type===ORDER_TYPE_BUY){
|
||||
var allowbuy=Math.min(account.Balance/last,notcompleted);
|
||||
if (allowbuy>minestbuy[exname]){
|
||||
var limited_order_ID=retryBuy(exchange_c,orderdata.Price+price_step,allowbuy);
|
||||
Log("加价买入。"+orderdata.Price+"|"+(orderdata.Price+price_step));
|
||||
var limited_order_data={
|
||||
exchange:exchange_c,
|
||||
ID:limited_order_ID,
|
||||
create_time:new Date(),
|
||||
utcdate:get_ChinaTimeString()
|
||||
};
|
||||
limit_orders_new.push(limited_order_data);
|
||||
}
|
||||
approximate_profit-=(orderdata.Price*orderdata.DealAmount*(1+handfee[exname]));
|
||||
dealamount_change+=(orderdata.DealAmount);
|
||||
}else if (type===ORDER_TYPE_SELL){
|
||||
var allowsell=Math.min(account.Stocks,notcompleted);
|
||||
if (allowsell>minestbuy[exname]){
|
||||
var limited_order_ID=retrySell(exchange_c,orderdata.Price-price_step,allowsell);
|
||||
Log("降价卖出。"+orderdata.Price+"|"+(orderdata.Price-price_step));
|
||||
var limited_order_data={
|
||||
exchange:exchange_c,
|
||||
ID:limited_order_ID,
|
||||
create_time:new Date(),
|
||||
utcdate:get_ChinaTimeString()
|
||||
};
|
||||
limit_orders_new.push(limited_order_data);
|
||||
}
|
||||
approximate_profit+=(orderdata.Price*orderdata.DealAmount*(1-handfee[exname]));
|
||||
dealamount_change-=(orderdata.DealAmount);
|
||||
}
|
||||
|
||||
//保存交易信息
|
||||
if (type===ORDER_TYPE_BUY){
|
||||
var details={
|
||||
type:"限价买",
|
||||
time:limit_orders[i].utcdate,
|
||||
RealAmount:orderdata.DealAmount,
|
||||
WantAmount:orderdata.Amount,
|
||||
RealPrice:orderdata.Price,
|
||||
WantPrice:orderdata.Price,
|
||||
Memo:"部分完成",
|
||||
exname:exname
|
||||
};
|
||||
if (enable_traders_recorder){
|
||||
trades.push(details);
|
||||
}
|
||||
}else if (type===ORDER_TYPE_SELL){
|
||||
var details={
|
||||
type:"限价卖",
|
||||
time:limit_orders[i].utcdate,
|
||||
RealAmount:orderdata.DealAmount,
|
||||
WantAmount:orderdata.Amount,
|
||||
RealPrice:orderdata.Price,
|
||||
WantPrice:orderdata.Price,
|
||||
Memo:"部分完成",
|
||||
exname:exname
|
||||
};
|
||||
if (enable_traders_recorder){
|
||||
trades.push(details);
|
||||
}
|
||||
}
|
||||
}else{
|
||||
//保存交易信息
|
||||
if (type===ORDER_TYPE_BUY){
|
||||
approximate_profit-=(orderdata.Price*orderdata.DealAmount*(1+handfee[exname]));
|
||||
dealamount_change+=(orderdata.DealAmount);
|
||||
var details={
|
||||
type:"限价买",
|
||||
time:limit_orders[i].utcdate,
|
||||
RealAmount:orderdata.DealAmount,
|
||||
WantAmount:orderdata.Amount,
|
||||
RealPrice:orderdata.Price,
|
||||
WantPrice:orderdata.Price,
|
||||
Memo:"已完成",
|
||||
exname:exname
|
||||
};
|
||||
if (enable_traders_recorder){
|
||||
trades.push(details);
|
||||
}
|
||||
}else if (type===ORDER_TYPE_SELL){
|
||||
approximate_profit+=(orderdata.Price*orderdata.DealAmount*(1-handfee[exname]));
|
||||
dealamount_change-=(orderdata.DealAmount);
|
||||
var details={
|
||||
type:"限价卖",
|
||||
time:limit_orders[i].utcdate,
|
||||
RealAmount:orderdata.DealAmount,
|
||||
WantAmount:orderdata.Amount,
|
||||
RealPrice:orderdata.Price,
|
||||
WantPrice:orderdata.Price,
|
||||
Memo:"已完成",
|
||||
exname:exname
|
||||
};
|
||||
if (enable_traders_recorder){
|
||||
trades.push(details);
|
||||
}
|
||||
}
|
||||
}
|
||||
}else{
|
||||
limit_orders_new.push(limit_orders[i]);
|
||||
}
|
||||
}
|
||||
limit_orders=limit_orders_new;
|
||||
}
|
||||
|
||||
//重试购买,直到成功返回
|
||||
function retryBuy(ex,price,num)
|
||||
{
|
||||
var r=ex.Buy(_N(price,price_n), _N(num,num_n));
|
||||
while (!r){
|
||||
Log("Buy失败,正在retry。");
|
||||
Sleep(wait_ms);
|
||||
r=ex.Buy(_N(price,price_n), _N(num,num_n));
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
//重试卖出,直到成功返回
|
||||
function retrySell(ex,price,num)
|
||||
{
|
||||
var r=ex.Sell(_N(price,price_n), _N(num,num_n));
|
||||
while (!r){
|
||||
Log("Sell失败,正在retry。");
|
||||
Sleep(wait_ms);
|
||||
r=ex.Sell(_N(price,price_n), _N(num,num_n));
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
//get utc+8 date string
|
||||
function get_ChinaTimeString()
|
||||
{
|
||||
var date = new Date();
|
||||
var now_utc = Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate(),date.getUTCHours(), date.getUTCMinutes(), date.getUTCSeconds());
|
||||
var cdate=new Date(now_utc);
|
||||
cdate.setHours(cdate.getHours()+8);
|
||||
var localstring=cdate.getFullYear()+'/'+(cdate.getMonth()+1)+'/'+cdate.getDate()+' '+cdate.getHours()+':'+cdate.getMinutes()+':'+cdate.getSeconds();
|
||||
return localstring;
|
||||
}
|
||||
|
||||
|
||||
//主逻辑
|
||||
function main(){
|
||||
|
||||
_CDelay(wait_ms);
|
||||
var exchange_num=exchanges.length;
|
||||
var pre_time = new Date();
|
||||
var start_time=new Date();
|
||||
var last_profit=0;
|
||||
var pre_profit=Number(_G("pre_profit"));
|
||||
Log('之前收入累计:'+pre_profit);
|
||||
|
||||
while(true){
|
||||
var cur_time = new Date();
|
||||
var passedtime=cur_time-pre_time;
|
||||
pre_time=cur_time;
|
||||
|
||||
for (var i=0; i<exchange_num-1; ++i)
|
||||
{
|
||||
for (var j=i+1;j<exchange_num; ++j)
|
||||
{
|
||||
banzhuan(exchanges[i],exchanges[j]);
|
||||
banzhuan(exchanges[j],exchanges[i]);
|
||||
}
|
||||
}
|
||||
|
||||
process_limiteorders();
|
||||
|
||||
var total_orders_notcomplete=0;
|
||||
var total_balance=0;
|
||||
var table1 = {type: 'table', title: '账户信息', cols: ['交易所', '最新价','可用币','可用余额','资产','手续费率','最小购买数量','未完成订单','实搬/搬砖机会','可买/可卖'], rows: []};
|
||||
var table2 = {type: 'table', title: '状态', cols: ['轮询时间','实际搬砖次数','错过搬砖次数','每天搬砖机会','当前总资产','【近似收益】','币数变化','未完成订单总数'], rows: []};
|
||||
var table3 = {type: 'table', title: '交易历史', cols: ['交易所','日期','类型', '成交数量','发单数量','成交价','发单价','备注'], rows: []};
|
||||
for (i=0; i<exchange_num; ++i){
|
||||
var exname=exchanges[i].GetName();
|
||||
var account=_C(exchanges[i].GetAccount);
|
||||
var ticker=_C(exchanges[i].GetTicker);
|
||||
var last=ticker.Last;
|
||||
var orders_not_complete=_C(exchanges[i].GetOrders);
|
||||
total_orders_notcomplete+=orders_not_complete.length;
|
||||
var balance=(account.Balance+account.FrozenBalance)+(account.Stocks+account.FrozenStocks)*last;
|
||||
total_balance+=balance;
|
||||
table1.rows.push([exname,last,
|
||||
account.Stocks+(account.Stocks<=warning_stocks?"#ff0000":"#00ff00"),
|
||||
account.Balance+(account.Balance<=warning_stocks*last?"#ff0000":"#00ff00"),
|
||||
balance,handfee[exname],minestbuy[exname],orders_not_complete.length,
|
||||
get_exchange_banzhuan(exname)+'/'+get_exchange_banzhuan(exname+"_op"),
|
||||
get_exchange_banzhuan(exname+"_canbuy")+'/'+get_exchange_banzhuan(exname+"_cansell")
|
||||
]);
|
||||
}
|
||||
table2.rows.push([passedtime+'ms',total_banzhuan,total_passed,
|
||||
total_op/((new Date()-start_time)/1000/60/60/24),
|
||||
total_balance+' '+exchanges[0].GetQuoteCurrency(),
|
||||
approximate_profit+' '+exchanges[0].GetQuoteCurrency(),
|
||||
dealamount_change, total_orders_notcomplete
|
||||
]);
|
||||
for (i=0; i < trades.length; i++){
|
||||
table3.rows.push([trades[i].exname,trades[i].time,trades[i].type,trades[i].RealAmount,trades[i].WantAmount,trades[i].RealPrice,trades[i].WantPrice,trades[i].Memo]);
|
||||
}
|
||||
|
||||
//show tables
|
||||
LogStatus('`' + JSON.stringify([table1,table2,table3])+'`'+'\n'+
|
||||
' *提示:红色代表余额可能不足。'+'\n'+
|
||||
'定制策略请联系微信: alinwo (验证消息: botvs)#0000ff'+'\n');
|
||||
|
||||
//show profit chart
|
||||
if (last_profit!==approximate_profit && total_orders_notcomplete===0 && limit_orders.length===0){
|
||||
LogProfit(pre_profit+approximate_profit);
|
||||
_G("pre_profit", pre_profit+approximate_profit);
|
||||
last_profit=approximate_profit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
```
|
||||
|
||||
> 策略出处
|
||||
|
||||
https://www.fmz.com/strategy/157267
|
||||
|
||||
> 更新时间
|
||||
|
||||
2019-07-16 14:06:06
|
||||
@@ -0,0 +1,72 @@
|
||||
|
||||
> 策略名称
|
||||
|
||||
简单多品种商品期货均线策略 (Copy)
|
||||
|
||||
> 策略作者
|
||||
|
||||
ktz
|
||||
|
||||
> 策略描述
|
||||
|
||||
商品期货类库实现了$.CTA函数的策略框架,借助该策略框架, 可以短短几十行实现一个并发稳定可实盘的多品种策略
|
||||
|
||||
https://dn-filebox.qbox.me/21384f4e53937bc1dd252ff478ff08e5b4d52e71.png
|
||||
|
||||
> 策略参数
|
||||
|
||||
|
||||
|
||||
|参数|默认值|描述|
|
||||
|----|----|----|
|
||||
|Symbols|rb000,MA000|操作品种|
|
||||
|FastPeriod|5|快线周期|
|
||||
|SlowPeriod|20|慢线周期|
|
||||
|ConfirmPeriod|2|确认周期|
|
||||
|Lots|true|开仓手数|
|
||||
|
||||
|
||||
> 源码 (javascript)
|
||||
|
||||
``` javascript
|
||||
/*backtest
|
||||
start: 2017-06-01 00:00:00
|
||||
end: 2017-10-01 00:00:00
|
||||
period: 1d
|
||||
*/
|
||||
function main() {
|
||||
// 使用了商品期货类库的CTA策略框架
|
||||
$.CTA(Symbols, function(st) {
|
||||
var r = st.records
|
||||
var mp = st.position.amount
|
||||
var symbol = st.symbol
|
||||
/*
|
||||
r为K线, mp为当前品种持仓数量, 正数指多仓, 负数指空仓, 0则不持仓, symbol指品种名称
|
||||
返回值如为n:
|
||||
n = 0 : 指全部平仓(不管当前持多持空)
|
||||
n > 0 : 如果当前持多仓,则加n个多仓, 如果当前为空仓则平n个空仓,如果n大于当前持仓, 则反手开多仓
|
||||
n < 0 : 如果当前持空仓,则加n个空仓, 如果当前为多仓则平n个多仓,如果-n大于当前持仓, 则反手开空仓
|
||||
无返回值表示什么也不做
|
||||
*/
|
||||
if (r.length < SlowPeriod) {
|
||||
return
|
||||
}
|
||||
var cross = _Cross(TA.EMA(r, FastPeriod), TA.EMA(r, SlowPeriod));
|
||||
if (mp <= 0 && cross > ConfirmPeriod) {
|
||||
Log(symbol, "金叉周期", cross, "当前持仓", mp);
|
||||
return Lots * (mp < 0 ? 2 : 1)
|
||||
} else if (mp >= 0 && cross < -ConfirmPeriod) {
|
||||
Log(symbol, "死叉周期", cross, "当前持仓", mp);
|
||||
return -Lots * (mp > 0 ? 2 : 1)
|
||||
}
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
> 策略出处
|
||||
|
||||
https://www.fmz.com/strategy/159506
|
||||
|
||||
> 更新时间
|
||||
|
||||
2019-07-30 22:26:44
|
||||
@@ -0,0 +1,250 @@
|
||||
|
||||
> 策略名称
|
||||
|
||||
网格策略
|
||||
|
||||
> 策略作者
|
||||
|
||||
alinwo
|
||||
|
||||
> 策略描述
|
||||
|
||||
什么是网格交易策略?
|
||||
这是一种仓位策略,用于动态调仓。
|
||||
|
||||
所谓网格交易法(grid trading method),也称鱼网交易网,指以某点为基点,每上涨或下跌一定点数挂一定数量空单或多单,设定盈利目标,但不设止损,当价格朝期望方向进展时获利平仓,并在原点位挂同样的买单或卖单。
|
||||
|
||||
基本概念
|
||||
1、底仓价:价格的标准线,建仓和调仓的重要依据。
|
||||
2、低吸高抛:仓位控制贯彻低吸高抛,绝不追涨杀跌。
|
||||
|
||||
下面举个例子:
|
||||
操作:在底仓价的附近,我们根据网格的大小,比如每跌3%按仓位买入(第一档:买40%,第二档:买30%,第三档:买20%,第四档:买10%)。
|
||||
|
||||
要注意的是,这里买卖不是绝对的定量,而是调仓到对应仓位。如果第一次跌破3%,而后上涨到5%时,是不操作的,因为下跌时只建了40%的仓,而上涨5%的仓位是60%,不够抛出。
|
||||
|
||||
数字货币中应用网格策略
|
||||
借助发明者平台,我们可以通过JS代码完整实现网格交易策略,策略实现选中币种价格上涨时分批次建仓以及币价下跌时分批次减仓。
|
||||
|
||||
策略参数详情
|
||||
https://www.pcclean.io/%e6%95%b0%e5%ad%97%e8%b4%a7%e5%b8%81%e9%87%8f%e5%8c%96%e7%ad%96%e7%95%a5%e4%b9%8b%e7%bd%91%e6%a0%bc%e7%ad%96%e7%95%a5/
|
||||
|
||||
|
||||
|
||||
> 源码 (javascript)
|
||||
|
||||
``` javascript
|
||||
var KaiCangPercent=0.5; //开仓百分比
|
||||
var PriceChangeStep=0.005; //网格交易中的价格改变量(百分比)
|
||||
var StocksChangeStep=0.2; //网格交易中的股票改变量(百分比)
|
||||
|
||||
var minBalance=80;
|
||||
var minStocks=0.001;
|
||||
var IsJianCang=false; //是否建仓
|
||||
var LastOperationPrice=null; //网格交易中最近处理的股票价格
|
||||
var PriceChangeStep_Real=null; //网格交易中的价格改变量(实际值)
|
||||
var StocksChangeStep_Real=null;//网格交易中的股票改变量(实际值)
|
||||
|
||||
function getAccount(){
|
||||
var curAccount = exchange.GetAccount();
|
||||
while(!curAccount){
|
||||
Log("GetAccount() Retrying...");
|
||||
Sleep(2000);
|
||||
nowAccount = exchange.GetAccount();
|
||||
}
|
||||
return curAccount;
|
||||
}
|
||||
|
||||
function getTicker(){
|
||||
var curTicker=exchange.GetTicker();
|
||||
while(!curTicker){
|
||||
Log("GetTicker() Retrying...");
|
||||
Sleep(2000);
|
||||
curTicker=exchange.GetTicker();
|
||||
}
|
||||
return curTicker;
|
||||
}
|
||||
|
||||
//type: PERIOD_M5 etc...
|
||||
function getRecords(type){
|
||||
var records = exchange.GetRecords(type);
|
||||
while(!records || records.length < 100)
|
||||
{
|
||||
Log("GetRecords() retrying...");
|
||||
Sleep(2000);
|
||||
records = exchange.GetRecords(type);
|
||||
}
|
||||
return records;
|
||||
}
|
||||
|
||||
function printProfit(){
|
||||
try {
|
||||
var curAccount = getAccount();
|
||||
var currrentPrice = getTicker().Last; //当前价格
|
||||
LogProfit(curAccount.Balance + curAccount.FrozenBalance + (curAccount.Stocks + curAccount.FrozenStocks) * currrentPrice);
|
||||
} catch (e) {
|
||||
Log("LogProfit error:" + e);
|
||||
}
|
||||
}
|
||||
|
||||
function EMAChecker(){
|
||||
//获得records
|
||||
var records = getRecords(PERIOD_H1);
|
||||
var len = records.length;
|
||||
//获得ema
|
||||
var ret1=TA.EMA(records,5);
|
||||
var ret2=TA.EMA(records,15);
|
||||
var emaFast=ret1[len - 1];
|
||||
var emaSlow=ret2[len - 1];
|
||||
if (emaFast-emaSlow>=5){
|
||||
return 1;//牛市
|
||||
}
|
||||
else if(emaFast-emaSlow<=-5){
|
||||
return -1;//熊市
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
function MACDChecker(){
|
||||
//获得records
|
||||
var records = getRecords(PERIOD_H1);
|
||||
var len = records.length;
|
||||
|
||||
//获得当前MACD指标
|
||||
var ret = TA.MACD(records, 12, 26, 9);
|
||||
var dif = ret[0];
|
||||
var dea = ret[1];
|
||||
var macd = ret[2];
|
||||
|
||||
//获得KDJ
|
||||
var ret2 = TA.KDJ(records, 9, 3, 3);
|
||||
var vark = ret2[0];
|
||||
var vard = ret2[1];
|
||||
var varj = ret2[2];
|
||||
|
||||
if (macd[len - 1]>=0.2){
|
||||
return 1;//牛市
|
||||
}
|
||||
else if (macd[len - 1]<=-0.2){
|
||||
return -1;//熊市
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
//amount: CNY amount
|
||||
function BuyAndPrintProfit(amount){
|
||||
var nowAccount = getAccount();
|
||||
if (nowAccount.Balance > minBalance) {
|
||||
exchange.Buy(amount);
|
||||
printProfit();
|
||||
}
|
||||
}
|
||||
|
||||
function SellAndPrintProfit(stocksAmount){
|
||||
var nowAccount = getAccount();
|
||||
if (nowAccount.Stocks > minStocks) {
|
||||
exchange.Sell(stocksAmount);
|
||||
printProfit();
|
||||
}
|
||||
}
|
||||
|
||||
function BuyPercent(percent){
|
||||
var nowAccount = getAccount();
|
||||
var nowPrice = getTicker().Last;
|
||||
var totalAssets=nowAccount.Balance + nowAccount.FrozenBalance + (nowAccount.Stocks + nowAccount.FrozenStocks) * nowPrice;
|
||||
var canOperateAssets=parseFloat((totalAssets*percent).toFixed(2));
|
||||
var realBuyAmount=(canOperateAssets<nowAccount.Balance?canOperateAssets:nowAccount.Balance);
|
||||
BuyAndPrintProfit(realBuyAmount);
|
||||
return (realBuyAmount/nowPrice);
|
||||
}
|
||||
|
||||
function ObjToString(object){
|
||||
var output = '';
|
||||
for (var property in object) {
|
||||
output += property + ': ' + object[property]+'; ';
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
function startTable(){
|
||||
//开仓
|
||||
var stocksAmount=BuyPercent(KaiCangPercent);
|
||||
IsJianCang=true;
|
||||
var nowPrice = getTicker().Last;
|
||||
var nowAccount = getAccount();
|
||||
PriceChangeStep_Real=nowPrice*PriceChangeStep;
|
||||
StocksChangeStep_Real=stocksAmount*StocksChangeStep;
|
||||
LastOperationPrice=nowPrice;
|
||||
|
||||
var logItem={
|
||||
"开仓百分比":KaiCangPercent*100+"%",
|
||||
"价格步长":PriceChangeStep_Real,
|
||||
"币量步长":StocksChangeStep_Real,
|
||||
"开仓价":nowPrice
|
||||
};
|
||||
Log("布网完成!"+ObjToString(logItem));
|
||||
}
|
||||
|
||||
function NiuShi_Do(){
|
||||
if (!IsJianCang){
|
||||
startTable();
|
||||
}else{
|
||||
//按网格买卖
|
||||
var nowPrice = getTicker().Last;
|
||||
if (nowPrice-LastOperationPrice>0){//涨
|
||||
var n=Math.floor((nowPrice-LastOperationPrice)/PriceChangeStep_Real);
|
||||
if (n>0){
|
||||
LastOperationPrice=LastOperationPrice+n*PriceChangeStep_Real;
|
||||
SellAndPrintProfit(n*StocksChangeStep_Real);
|
||||
|
||||
//如果没有库存了
|
||||
var nowAccount = getAccount();
|
||||
if ((nowAccount.Stocks + nowAccount.FrozenStocks) <= minStocks){
|
||||
Log("涨幅过大?已经没有库存了。");
|
||||
startTable();
|
||||
}
|
||||
}
|
||||
}else if (nowPrice-LastOperationPrice<0){//跌
|
||||
var n=Math.floor((LastOperationPrice-nowPrice)/PriceChangeStep_Real);
|
||||
if (n>0){
|
||||
LastOperationPrice=LastOperationPrice-n*PriceChangeStep_Real;
|
||||
BuyAndPrintProfit(n*StocksChangeStep_Real*nowPrice);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function XiongShi_Do(){
|
||||
//清仓
|
||||
var nowAccount = getAccount();
|
||||
SellAndPrintProfit(nowAccount.Stocks);
|
||||
IsJianCang=false;
|
||||
}
|
||||
|
||||
function main() {
|
||||
Log(exchange.GetAccount());
|
||||
|
||||
while (true) {
|
||||
|
||||
//确定是否在MACD牛市范围
|
||||
var result1=MACDChecker();
|
||||
var result2=EMAChecker();
|
||||
if (result1===1 && result2===1){
|
||||
NiuShi_Do();
|
||||
}else if (result1===-1 || result2===-1){
|
||||
XiongShi_Do();
|
||||
}
|
||||
|
||||
//等待下次查询交易所
|
||||
Sleep(15*1000);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
> 策略出处
|
||||
|
||||
https://www.fmz.com/strategy/156663
|
||||
|
||||
> 更新时间
|
||||
|
||||
2019-07-11 21:52:57
|
||||
@@ -0,0 +1,54 @@
|
||||
|
||||
> 策略名称
|
||||
|
||||
肯特纳通道升级版金肯特纳kingkeltner策略
|
||||
|
||||
> 策略作者
|
||||
|
||||
Hukybo
|
||||
|
||||
> 策略描述
|
||||
|
||||
**策略简介**
|
||||
肯特纳通道是由Chester W. Keltner在上个世纪60年代发明的一个交易系统,其核心思想是均线理论。并且当时该系统在非常长的一段时间内,得到了令人瞩目的成绩。虽然原版的肯特纳通道系统没有刚出现时那么有效,但它的核心思想,至今都对交易界产生很深远的影响。
|
||||
[点击阅读更多内容](https://www.fmz.com/bbs-topic/4104)
|
||||
|
||||
|
||||
|
||||
> 源码 (麦语言)
|
||||
|
||||
``` pascal
|
||||
(*backtest
|
||||
start: 2019-01-01 00:00:00
|
||||
end: 2019-07-27 00:00:00
|
||||
period: 1h
|
||||
exchanges: [{"eid":"Futures_BitMEX","currency":"XBT_USD"}]
|
||||
args: [["MAN",20],["TradeAmount",10000,126961],["SlideTick",2,126961],["ContractType","XBTUSD",126961]]
|
||||
*)
|
||||
|
||||
// 参数
|
||||
MAN:=20;
|
||||
ATRN:=50;
|
||||
|
||||
JG:=(HIGH+LOW+CLOSE)/3; // 基础价格
|
||||
ZG^^MA(JG,MAN); // 中轨
|
||||
TRUEHIGH1:=IF(HIGH>REF(C,1),HIGH,REF(C,1));
|
||||
TRUELOW1:=IF(LOW<=REF(C,1),LOW,REF(C,1));
|
||||
TRUERANGE1:=IF(ISLASTBAR,H-L,TRUEHIGH1-TRUELOW1); // 计算真实波动幅度
|
||||
SG^^ZG+MA(TRUERANGE1,ATRN); // 上轨
|
||||
XG^^ZG-MA(TRUERANGE1,ATRN); // 下轨
|
||||
|
||||
ZG>REF(ZG,1)&&C>SG,BK; // 中轨向上,并且价格升破上轨,开多单
|
||||
C<ZG,SP; // 持有多单时,价格跌破中轨,平多单
|
||||
ZG<REF(ZG,1)&&C<XG,SK; // 中轨向下,并且价格跌破下轨,开空单
|
||||
C>ZG,BP; // 持有空单时,价格升破中轨,平空单
|
||||
AUTOFILTER; // 设置信号过滤方式
|
||||
```
|
||||
|
||||
> 策略出处
|
||||
|
||||
https://www.fmz.com/strategy/159285
|
||||
|
||||
> 更新时间
|
||||
|
||||
2019-07-27 16:28:56
|
||||
@@ -0,0 +1,43 @@
|
||||
|
||||
> 策略名称
|
||||
|
||||
能量潮OBV在量化交易中的详细用法和实战技巧
|
||||
|
||||
> 策略作者
|
||||
|
||||
Hukybo
|
||||
|
||||
> 策略描述
|
||||
|
||||
**策略简介**
|
||||
古代打仗有一句这样的成语:“兵马未动,粮草先行”。在交易中也有一句类似的话:“量在价先”。意思是说:价格的上涨和下跌,背后都是成交量在推动,一切风吹草动,最先反应在成交量上。于是呢,就有人发明了衡量成交量的能量潮指标(On Balance Volume,OBV)。
|
||||
|
||||
能量潮(OBV)是上个世纪60年代由格兰维尔(Joe Granville)发明,虽然其中的算法很简单,但是在交易的“价、量、时、空”四个要素中,以“量”为切入点,得到了很多交易者的青睐。作为交易市场中的人气指标,能量潮可以很直观的反应出价格与成交量的相互关系,从而帮助交易者从更多的角度观察市场。
|
||||
|
||||
[点击查看更多内容](https://www.fmz.com/bbs-topic/4118)
|
||||
|
||||
|
||||
|
||||
> 源码 (麦语言)
|
||||
|
||||
``` pascal
|
||||
N:=10;
|
||||
OBV:=SUM(IFELSE(CLOSE>REF(CLOSE,1),VOL,IFELSE(CLOSE<REF(CLOSE,1),-VOL,0)),0);
|
||||
TR:=MAX(MAX((HIGH-LOW),ABS(REF(CLOSE,1)-HIGH)),ABS(REF(CLOSE,1)-LOW));
|
||||
ATR:=MA(TR,N);
|
||||
B:EMA2(OBV*ATR,N);
|
||||
D:EMA2(OBV*ATR,N*2);
|
||||
B>REF(B,1) && D>REF(D,1) && B>D,BK;
|
||||
B<REF(B,1) || D<REF(D,1) || B<D,SP;
|
||||
B<REF(B,1) && D<REF(D,1) && B<D,SK;
|
||||
B>REF(B,1) || D>REF(D,1) || B>D,BP;
|
||||
AUTOFILTER;
|
||||
```
|
||||
|
||||
> 策略出处
|
||||
|
||||
https://www.fmz.com/strategy/159997
|
||||
|
||||
> 更新时间
|
||||
|
||||
2019-07-31 19:44:46
|
||||
@@ -0,0 +1,35 @@
|
||||
|
||||
> 策略名称
|
||||
|
||||
获取USDT和USD的实时汇率
|
||||
|
||||
> 策略作者
|
||||
|
||||
affroad
|
||||
|
||||
> 策略描述
|
||||
|
||||
通过Http请求读取coinmarketcap上的实时USDT汇率
|
||||
|
||||
我的博客:https://www.pcclean.io/yax2
|
||||
|
||||
|
||||
|
||||
> 源码 (javascript)
|
||||
|
||||
``` javascript
|
||||
//获得美元汇率
|
||||
function getUSDTratio(){
|
||||
var r = _C(HttpQuery,"https://api.coinmarketcap.com/v2/ticker/825/");
|
||||
var o = JSON.parse(r);
|
||||
return o.data.quotes.USD.price;
|
||||
}
|
||||
```
|
||||
|
||||
> 策略出处
|
||||
|
||||
https://www.fmz.com/strategy/157364
|
||||
|
||||
> 更新时间
|
||||
|
||||
2019-07-16 14:21:58
|
||||
@@ -0,0 +1,50 @@
|
||||
|
||||
> 策略名称
|
||||
|
||||
钉钉推送
|
||||
|
||||
> 策略作者
|
||||
|
||||
jason47
|
||||
|
||||
> 策略描述
|
||||
|
||||
- $.ddNotice(title, content)
|
||||
> title: 字符串类型,推送的标题
|
||||
> content: 数组类型,markdown格式
|
||||
|
||||
> 策略参数
|
||||
|
||||
|
||||
|
||||
|参数|默认值|描述|
|
||||
|----|----|----|
|
||||
|DING_URI|钉钉URI|钉钉URI|
|
||||
|
||||
|
||||
> 源码 (javascript)
|
||||
|
||||
``` javascript
|
||||
$.ddNotice = function(title, content) {
|
||||
content = content == null ? title : content instanceof Array ? content.join('\n') : content
|
||||
HttpQuery(DING_URI, {
|
||||
method: 'POST',
|
||||
timeout: 3000,
|
||||
data: JSON.stringify({
|
||||
msgtype: 'markdown',
|
||||
markdown: {
|
||||
title: title,
|
||||
text: content
|
||||
}
|
||||
})
|
||||
}, null, 'Content-Type: application/json')
|
||||
}
|
||||
```
|
||||
|
||||
> 策略出处
|
||||
|
||||
https://www.fmz.com/strategy/159668
|
||||
|
||||
> 更新时间
|
||||
|
||||
2019-08-13 22:38:39
|
||||
Reference in New Issue
Block a user